Merge: ALSA - update drivers for 9.9 - upstream 7.1.5 (stable)

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8435

JIRA: https://issues.redhat.com/browse/RHEL-193254

This upstream patchset updates the ALSA driver code to upstream stable 7.1.5 kernel.

Omitted-fix: dd1bfaf9413e9c8a0fcfb45dcb735c6768a45251   # see commit - this revert is for 7.2+ kernel code

Omitted-fix: 99c159279c6dfa2c4867c7f76875f58263f8f43b   # used hash 225d70b8074502acee3943bf0c2e839e867cd38c for backport - already in RHEL kernel

Signed-off-by: Jaroslav Kysela <jkysela@redhat.com>

Approved-by: Krzysztof Pawlinski <kpawlins@redhat.com>
Approved-by: Desnes Nunes <desnesn@redhat.com>
Approved-by: Tony Camuso <tcamuso@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot
2026-08-17 08:11:14 +00:00
66 changed files with 1054 additions and 341 deletions
@@ -2383,6 +2383,12 @@ quirk_flags
``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24
* bit 28: ``mixer_capture_linear_vol``
Similar to bit 27 but for capture streams. Overrides bit 25
* bit 29: ``ifb_silence_on_empty``
In implicit feedback mode, when an entire capture URB returns with
all iso_frame_desc[i].status != 0 (bytes==0), do not silently return
from snd_usb_handle_sync_urb. Instead fall through and enqueue a
packet_info containing only size-0 packets, so the OUT ring keeps
moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
This module supports multiple devices, autoprobe and hotplugging.
+64 -4
View File
@@ -1133,6 +1133,7 @@ EXPORT_SYMBOL(release_firmware);
/* Async support */
struct firmware_work {
struct work_struct work;
struct list_head list;
struct module *module;
const char *name;
struct device *device;
@@ -1141,6 +1142,17 @@ struct firmware_work {
u32 opt_flags;
};
static LIST_HEAD(firmware_work_list);
static DEFINE_SPINLOCK(firmware_work_lock);
static void firmware_work_free(struct firmware_work *fw_work)
{
put_device(fw_work->device); /* taken in request_firmware_nowait() */
module_put(fw_work->module);
kfree_const(fw_work->name);
kfree(fw_work);
}
static void request_firmware_work_func(struct work_struct *work)
{
struct firmware_work *fw_work;
@@ -1151,11 +1163,15 @@ static void request_firmware_work_func(struct work_struct *work)
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
fw_work->opt_flags);
fw_work->cont(fw, fw_work->context);
put_device(fw_work->device); /* taken in request_firmware_nowait() */
module_put(fw_work->module);
kfree_const(fw_work->name);
kfree(fw_work);
spin_lock_irq(&firmware_work_lock);
if (!list_empty(&fw_work->list)) {
list_del_init(&fw_work->list);
spin_unlock_irq(&firmware_work_lock);
firmware_work_free(fw_work);
return;
}
spin_unlock_irq(&firmware_work_lock);
}
@@ -1165,6 +1181,7 @@ static int _request_firmware_nowait(
void (*cont)(const struct firmware *fw, void *context), bool nowarn)
{
struct firmware_work *fw_work;
unsigned long flags;
fw_work = kzalloc(sizeof(struct firmware_work), gfp);
if (!fw_work)
@@ -1197,7 +1214,12 @@ static int _request_firmware_nowait(
get_device(fw_work->device);
INIT_WORK(&fw_work->work, request_firmware_work_func);
spin_lock_irqsave(&firmware_work_lock, flags);
list_add_tail(&fw_work->list, &firmware_work_list);
schedule_work(&fw_work->work);
spin_unlock_irqrestore(&firmware_work_lock, flags);
return 0;
}
@@ -1260,6 +1282,44 @@ int firmware_request_nowait_nowarn(
}
EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn);
/**
* request_firmware_nowait_cancel() - cancel an async firmware request
* @device: device for which the firmware is being loaded
* @context: context passed to request_firmware_nowait()
* @cont: callback passed to request_firmware_nowait()
*
* Cancel a pending request_firmware_nowait() request for @device, @context
* and @cont. If the associated work has already started, this function waits
* until the callback has returned. If the callback has already completed, this
* function does nothing.
*
* This function may sleep.
*/
void request_firmware_nowait_cancel(struct device *device, void *context,
void (*cont)(const struct firmware *fw,
void *context))
{
struct firmware_work *fw_work = NULL;
struct firmware_work *tmp;
spin_lock_irq(&firmware_work_lock);
list_for_each_entry_reverse(tmp, &firmware_work_list, list) {
if (tmp->device == device && tmp->context == context &&
tmp->cont == cont) {
fw_work = tmp;
list_del_init(&fw_work->list);
break;
}
}
spin_unlock_irq(&firmware_work_lock);
if (!fw_work)
return;
cancel_work_sync(&fw_work->work);
firmware_work_free(fw_work);
}
EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel);
#ifdef CONFIG_FW_CACHE
static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
+1 -1
View File
@@ -722,7 +722,7 @@ static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *con
unsigned int loadaddr, val;
int ret;
if (!firmware) {
if (!firmware || firmware->size < sizeof(*hdr)) {
dev_err(cs42l43->dev, "Failed to load firmware\n");
cs42l43->firmware_error = -ENODEV;
goto err;
@@ -299,39 +299,36 @@ static int sdw_add_element_group_count(struct sdw_group *group,
int num = group->count;
int i;
for (i = 0; i <= num; i++) {
for (i = 0; i < num; i++) {
if (rate == group->rates[i] && lane == group->lanes[i])
break;
if (i != num)
continue;
if (group->count >= group->max_size) {
unsigned int *rates;
unsigned int *lanes;
group->max_size += 1;
rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!rates)
return -ENOMEM;
group->rates = rates;
lanes = krealloc(group->lanes,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!lanes)
return -ENOMEM;
group->lanes = lanes;
}
group->rates[group->count] = rate;
group->lanes[group->count++] = lane;
return 0;
}
if (group->count >= group->max_size) {
unsigned int *rates;
unsigned int *lanes;
group->max_size += 1;
rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!rates)
return -ENOMEM;
group->rates = rates;
lanes = krealloc(group->lanes,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!lanes)
return -ENOMEM;
group->lanes = lanes;
}
group->rates[group->count] = rate;
group->lanes[group->count++] = lane;
return 0;
}
+1
View File
@@ -317,6 +317,7 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
__func__, ret);
sdw_release_stream(cdns->bus.bpt_stream);
cdns->bus.bpt_stream = NULL;
}
+7
View File
@@ -697,6 +697,13 @@ static int sdw_program_params(struct sdw_bus *bus, bool prepare)
if (scale_index < 0)
return scale_index;
/* Skip the unattached Peripherals */
if (!completion_done(&slave->enumeration_complete)) {
dev_warn(&slave->dev,
"Not enumerated, skip programming BUSCLOCK_SCALE\n");
continue;
}
ret = sdw_write_no_pm(slave, addr1, scale_index);
if (ret < 0) {
dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n");
+10
View File
@@ -109,6 +109,9 @@ int request_firmware_nowait(
struct module *module, bool uevent,
const char *name, struct device *device, gfp_t gfp, void *context,
void (*cont)(const struct firmware *fw, void *context));
void request_firmware_nowait_cancel(struct device *device, void *context,
void (*cont)(const struct firmware *fw,
void *context));
int request_firmware_direct(const struct firmware **fw, const char *name,
struct device *device);
int request_firmware_into_buf(const struct firmware **firmware_p,
@@ -156,6 +159,13 @@ static inline int request_firmware_nowait(
return -EINVAL;
}
static inline void request_firmware_nowait_cancel(struct device *device,
void *context,
void (*cont)(const struct firmware *fw,
void *context))
{
}
static inline void release_firmware(const struct firmware *fw)
{
}
+1
View File
@@ -685,6 +685,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm);
int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin);
int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin);
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
bool snd_soc_dapm_pin_has_prefix(struct snd_soc_card *card, const char *pin);
void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card);
/* dapm path query */
+8 -3
View File
@@ -1139,7 +1139,7 @@ EXPORT_SYMBOL(snd_card_file_remove);
* typically around calling control ops.
*
* 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.
* when this function returns 0.
*
* Return: Zero if successful, or a negative error code.
*/
@@ -1152,7 +1152,11 @@ int snd_power_ref_and_wait(struct snd_card *card)
card->shutdown ||
snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
snd_power_unref(card), snd_power_ref(card));
return card->shutdown ? -ENODEV : 0;
if (card->shutdown) {
snd_power_unref(card);
return -ENODEV;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
@@ -1169,7 +1173,8 @@ int snd_power_wait(struct snd_card *card)
int ret;
ret = snd_power_ref_and_wait(card);
snd_power_unref(card);
if (!ret)
snd_power_unref(card);
return ret;
}
EXPORT_SYMBOL(snd_power_wait);
+4 -2
View File
@@ -39,8 +39,10 @@ static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct s
*/
int
snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q,
struct snd_seq_event *ev, snd_use_lock_t **lockp)
{
*lockp = NULL;
switch (q->s.code) {
case SEQ_EXTENDED:
return extended_event(dp, q, ev);
@@ -69,7 +71,7 @@ snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd
if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
break;
if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev, lockp);
break;
case SEQ_ECHO:
+2 -1
View File
@@ -91,7 +91,8 @@ union evrec {
#define ev_is_long(ev) ((ev)->s.code >= 128)
#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q,
struct snd_seq_event *ev, snd_use_lock_t **lockp);
int snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *q);
int snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop);
+4 -1
View File
@@ -45,14 +45,17 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
{
unsigned char ev[8];
struct snd_seq_event tmpev;
snd_use_lock_t *lock = NULL;
if (copy_from_user(ev, arg, 8))
return -EFAULT;
memset(&tmpev, 0, sizeof(tmpev));
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)) {
if (!snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev, &lock)) {
snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
if (lock)
snd_use_lock_free(lock);
}
return 0;
}
+5 -1
View File
@@ -593,7 +593,8 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
* non-zero : invalid - ignored
*/
int
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c,
struct snd_seq_event *ev, snd_use_lock_t **lockp)
{
struct seq_oss_midi *mdev __free(seq_oss_midi) =
get_mididev(dp, dev);
@@ -602,6 +603,9 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
return -ENODEV;
if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
/* the caller must release this later */
*lockp = &mdev->use_lock;
snd_use_lock_use(*lockp);
return 0;
}
return -EINVAL;
+1 -1
View File
@@ -26,7 +26,7 @@ void snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode);
int snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev);
void snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c,
struct snd_seq_event *ev);
struct snd_seq_event *ev, snd_use_lock_t **lockp);
int snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private);
int snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf);
+52 -25
View File
@@ -73,13 +73,17 @@ snd_seq_oss_readq_delete(struct seq_oss_readq *q)
void
snd_seq_oss_readq_clear(struct seq_oss_readq *q)
{
if (q->qlen) {
q->qlen = 0;
q->head = q->tail = 0;
scoped_guard(spinlock_irqsave, &q->lock) {
if (q->qlen) {
q->qlen = 0;
q->head = 0;
q->tail = 0;
}
q->input_time = (unsigned long)-1;
}
/* if someone sleeping, wake'em up */
wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
/*
@@ -136,11 +140,11 @@ int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
/*
* copy an event to input queue:
* return zero if enqueued
* caller must hold lock
*/
int
snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
static int snd_seq_oss_readq_put_event_locked(struct seq_oss_readq *q,
union evrec *ev)
{
guard(spinlock_irqsave)(&q->lock);
if (q->qlen >= q->maxlen - 1)
return -ENOMEM;
@@ -148,12 +152,27 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
q->tail = (q->tail + 1) % q->maxlen;
q->qlen++;
/* wake up sleeper */
wake_up(&q->midi_sleep);
return 0;
}
/*
* 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)
{
int rc;
scoped_guard(spinlock_irqsave, &q->lock) {
rc = snd_seq_oss_readq_put_event_locked(q, ev);
if (!rc)
wake_up(&q->midi_sleep);
}
return rc;
}
/*
* pop queue
@@ -209,23 +228,31 @@ snd_seq_oss_readq_poll(struct seq_oss_readq *q, struct file *file, poll_table *w
int
snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int seq_mode)
{
if (curt != q->input_time) {
union evrec rec;
memset(&rec, 0, sizeof(rec));
switch (seq_mode) {
case SNDRV_SEQ_OSS_MODE_SYNTH:
rec.echo = (curt << 8) | SEQ_WAIT;
snd_seq_oss_readq_put_event(q, &rec);
break;
case SNDRV_SEQ_OSS_MODE_MUSIC:
rec.t.code = EV_TIMING;
rec.t.cmd = TMR_WAIT_ABS;
rec.t.time = curt;
snd_seq_oss_readq_put_event(q, &rec);
break;
int queued = 0;
scoped_guard(spinlock_irqsave, &q->lock) {
if (curt != q->input_time) {
union evrec rec;
memset(&rec, 0, sizeof(rec));
switch (seq_mode) {
case SNDRV_SEQ_OSS_MODE_SYNTH:
rec.echo = (curt << 8) | SEQ_WAIT;
queued = !snd_seq_oss_readq_put_event_locked(q, &rec);
break;
case SNDRV_SEQ_OSS_MODE_MUSIC:
rec.t.code = EV_TIMING;
rec.t.cmd = TMR_WAIT_ABS;
rec.t.time = curt;
queued = !snd_seq_oss_readq_put_event_locked(q, &rec);
break;
}
q->input_time = curt;
}
q->input_time = curt;
}
if (queued)
wake_up(&q->midi_sleep);
return 0;
}
+4 -1
View File
@@ -153,6 +153,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
{
int rc = 0;
struct snd_seq_event event;
snd_use_lock_t *lock = NULL;
/* if this is a timing event, process the current time */
if (snd_seq_oss_process_timer_event(dp->timer, rec))
@@ -164,7 +165,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
event.type = SNDRV_SEQ_EVENT_NOTEOFF;
snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port);
if (snd_seq_oss_process_event(dp, rec, &event))
if (snd_seq_oss_process_event(dp, rec, &event, &lock))
return 0; /* invalid event - no need to insert queue */
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
@@ -173,6 +174,8 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
else
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt,
!is_nonblock_mode(dp->file_mode));
if (lock)
snd_use_lock_free(lock);
return rc;
}
+28 -5
View File
@@ -441,6 +441,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
memcpy(&tmpev, &cell->event, aligned_size);
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
tmpev.data.ext.ptr = NULL;
if (copy_to_user(buf, &tmpev, aligned_size)) {
err = -EFAULT;
break;
@@ -537,16 +538,38 @@ static int bounce_error_event(struct snd_seq_client *client,
/* set up quoted error */
memset(&bounce_ev, 0, sizeof(bounce_ev));
bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
if (client->type == USER_CLIENT) {
/*
* For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the
* original event embedded as variable-length data. This
* avoids exposing data.quote.event (a kernel pointer) to
* userspace. The variable-length path in snd_seq_event_dup()
* copies the event data from data.ext.ptr into chained cells,
* and snd_seq_expand_var_event() copies only the data content
* -- never the pointer -- to userspace.
*/
bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE;
bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
bounce_ev.data.ext.len = sizeof(struct snd_seq_event);
bounce_ev.data.ext.ptr = (char *)event;
} else {
/*
* For kernel clients, quote the event pointer directly.
* Kernel consumers can safely dereference the pointer.
*/
bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
bounce_ev.data.quote.origin = event->dest;
bounce_ev.data.quote.event = event;
bounce_ev.data.quote.value = -err; /* use positive value */
}
bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
bounce_ev.dest.client = client->number;
bounce_ev.dest.port = event->source.port;
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, atomic, hop + 1);
if (result < 0) {
client->event_lost++;
+37 -15
View File
@@ -101,13 +101,17 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
struct snd_seq_event *event)
{
struct snd_seq_event_cell *cell;
struct snd_seq_pool *pool;
bool linked;
int err;
if (snd_BUG_ON(!f))
return -EINVAL;
guard(snd_seq_fifo)(f);
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
retry:
pool = READ_ONCE(f->pool);
err = snd_seq_event_dup(pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
@@ -115,14 +119,24 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
}
/* append new cells to fifo */
linked = false;
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++;
if (cell->pool == f->pool) {
if (f->tail)
f->tail->next = cell;
f->tail = cell;
if (!f->head)
f->head = cell;
cell->next = NULL;
f->cells++;
linked = true;
}
}
if (!linked) {
/* Retry against the replacement pool after resize publishes it. */
snd_seq_cell_free(cell);
goto retry;
}
/* wakeup client */
@@ -194,13 +208,21 @@ 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)
{
bool linked = false;
if (cell) {
guard(spinlock_irqsave)(&f->lock);
cell->next = f->head;
f->head = cell;
if (!f->tail)
f->tail = cell;
f->cells++;
scoped_guard(spinlock_irqsave, &f->lock) {
if (cell->pool == f->pool) {
cell->next = f->head;
f->head = cell;
if (!f->tail)
f->tail = cell;
f->cells++;
linked = true;
}
}
if (!linked)
snd_seq_cell_free(cell);
}
}
@@ -237,7 +259,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
oldpool = f->pool;
oldhead = f->head;
/* exchange pools */
f->pool = newpool;
WRITE_ONCE(f->pool, newpool);
f->head = NULL;
f->tail = NULL;
f->cells = 0;
+1 -1
View File
@@ -211,7 +211,7 @@ int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
len -= offset;
if (len > count)
len = count;
err = expand_var_event(event, offset, count, buf, true);
err = expand_var_event(event, offset, len, buf, true);
if (err < 0)
return err;
return len;
+41 -14
View File
@@ -24,6 +24,7 @@ Possible options for midisynth module:
#include <sound/seq_device.h>
#include <sound/seq_midi_event.h>
#include <sound/initval.h>
#include "seq_lock.h"
MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
@@ -42,6 +43,8 @@ struct seq_midisynth {
int device;
int subdevice;
struct snd_rawmidi_file input_rfile;
spinlock_t output_lock; /* protects output_rfile publication */
snd_use_lock_t output_use_lock; /* in-flight event_input users */
struct snd_rawmidi_file output_rfile;
int seq_client;
int seq_port;
@@ -125,31 +128,42 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
struct seq_midisynth *msynth = private_data;
unsigned char msg[10]; /* buffer for constructing midi messages */
struct snd_rawmidi_substream *substream;
int err = 0;
int len;
if (snd_BUG_ON(!msynth))
return -EINVAL;
substream = msynth->output_rfile.output;
if (substream == NULL)
return -ENODEV;
scoped_guard(spinlock_irqsave, &msynth->output_lock) {
substream = msynth->output_rfile.output;
if (!substream)
return -ENODEV;
snd_use_lock_use(&msynth->output_use_lock);
}
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 */
pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
return 0;
goto out;
}
snd_seq_dump_var_event(ev, __dump_midi, substream);
snd_midi_event_reset_decode(msynth->parser);
} else {
if (msynth->parser == NULL)
return -EIO;
if (!msynth->parser) {
err = -EIO;
goto out;
}
len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
if (len < 0)
return 0;
goto out;
if (dump_midi(substream, msg, len) < 0)
snd_midi_event_reset_decode(msynth->parser);
}
return 0;
out:
snd_use_lock_free(&msynth->output_use_lock);
return err;
}
@@ -163,6 +177,8 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
msynth->card = card;
msynth->device = device;
msynth->subdevice = subdevice;
spin_lock_init(&msynth->output_lock);
snd_use_lock_init(&msynth->output_use_lock);
return 0;
}
@@ -215,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
{
int err;
struct seq_midisynth *msynth = private_data;
struct snd_rawmidi_file rfile = {};
struct snd_rawmidi_params params;
/* open midi port */
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_OUTPUT,
&msynth->output_rfile);
&rfile);
if (err < 0) {
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
@@ -229,12 +246,14 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
params.avail_min = 1;
params.buffer_size = output_buffer_size;
params.no_active_sensing = 1;
err = snd_rawmidi_output_params(msynth->output_rfile.output, &params);
err = snd_rawmidi_output_params(rfile.output, &params);
if (err < 0) {
snd_rawmidi_kernel_release(&msynth->output_rfile);
snd_rawmidi_kernel_release(&rfile);
return err;
}
snd_midi_event_reset_decode(msynth->parser);
scoped_guard(spinlock_irqsave, &msynth->output_lock)
msynth->output_rfile = rfile;
return 0;
}
@@ -242,11 +261,19 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
struct snd_rawmidi_file rfile = {};
if (snd_BUG_ON(!msynth->output_rfile.output))
scoped_guard(spinlock_irqsave, &msynth->output_lock) {
rfile = msynth->output_rfile;
msynth->output_rfile = (struct snd_rawmidi_file){};
}
if (snd_BUG_ON(!rfile.output))
return -EINVAL;
snd_rawmidi_drain_output(msynth->output_rfile.output);
return snd_rawmidi_kernel_release(&msynth->output_rfile);
snd_use_lock_sync(&msynth->output_use_lock);
snd_rawmidi_drain_output(rfile.output);
return snd_rawmidi_kernel_release(&rfile);
}
/* delete given midi synth port */
-1
View File
@@ -728,7 +728,6 @@ static void loopback_jiffies_timer_function(struct timer_list *t)
if (dpcm->period_update_pending) {
dpcm->period_update_pending = 0;
period_elapsed = true;
break;
}
}
}
-3
View File
@@ -162,9 +162,6 @@ 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.
*/
+3
View File
@@ -7730,6 +7730,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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 */
HDA_CODEC_QUIRK(0x17aa, 0x38a7, "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),
@@ -7847,6 +7848,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", 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),
@@ -592,6 +592,9 @@ static void tas2781_hda_unbind(struct device *dev,
comp->playback_hook = NULL;
}
request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv,
tasdev_fw_ready);
tas2781_hda_remove_controls(tas_hda);
tasdevice_config_info_remove(tas_hda->priv);
+11 -8
View File
@@ -190,15 +190,15 @@ static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
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);
}
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,
@@ -753,6 +753,9 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master,
comp->playback_hook = NULL;
}
request_firmware_nowait_cancel(tas_priv->dev, tas_priv,
tasdev_fw_ready);
tas2781_hda_remove_controls(tas_hda);
tasdevice_config_info_remove(tas_priv);
+1 -1
View File
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config SND_HDA_INTEL
tristate "HD Audio PCI"
depends on SND_PCI
depends on PCI
select SND_HDA
select SND_INTEL_DSP_CONFIG
help
+2 -2
View File
@@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8);
return snd_hdac_exec_verb(codec, verb, 0, val);
}
@@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) |
(val & 0xffff);
return snd_hdac_exec_verb(codec, verb, 0, NULL);
}
+4 -3
View File
@@ -303,13 +303,14 @@ static int create_sdw_dailink(struct snd_soc_card *card,
static int create_sdw_dailinks(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links, int *be_id,
struct asoc_sdw_dailink *soc_dais,
struct asoc_sdw_dailink *soc_dais, int num_dais,
struct snd_soc_codec_conf **codec_conf)
{
struct device *dev = card->dev;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
struct snd_soc_dai_link_component *sdw_platform_component;
int i;
int ret;
sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
@@ -329,7 +330,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
}
/* generate DAI links by each sdw link */
while (soc_dais->initialised) {
for (i = 0; i < num_dais && soc_dais->initialised; i++) {
int current_be_id = 0;
ret = create_sdw_dailink(card, soc_dais, dai_links,
@@ -463,7 +464,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
/* SDW */
if (sdw_be_num) {
ret = create_sdw_dailinks(card, &dai_links, &be_id,
soc_dais, &codec_conf);
soc_dais, num_ends, &codec_conf);
if (ret)
return ret;
}
+4 -3
View File
@@ -220,13 +220,14 @@ static int create_sdw_dailink(struct snd_soc_card *card,
static int create_sdw_dailinks(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links, int *be_id,
struct asoc_sdw_dailink *sof_dais,
struct asoc_sdw_dailink *sof_dais, int num_dais,
struct snd_soc_codec_conf **codec_conf)
{
int i;
int ret;
/* generate DAI links by each sdw link */
while (sof_dais->initialised) {
for (i = 0; i < num_dais && sof_dais->initialised; i++) {
int current_be_id = 0;
ret = create_sdw_dailink(card, sof_dais, dai_links,
@@ -334,7 +335,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
/* SDW */
if (sdw_be_num) {
ret = create_sdw_dailinks(card, &dai_links, &be_id,
sof_dais, &codec_conf);
sof_dais, num_ends, &codec_conf);
if (ret)
return ret;
}
+29
View File
@@ -692,8 +692,37 @@ static int snd_acp_runtime_resume(struct device *dev)
return acp_hw_runtime_resume(dev);
}
static void acp_disable_msi_on_resume(struct pci_dev *pdev)
{
u16 control;
if (!pdev->msi_cap)
return;
pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control);
if (control & PCI_MSI_FLAGS_ENABLE) {
dev_warn(&pdev->dev,
"ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n",
control);
control &= ~PCI_MSI_FLAGS_ENABLE;
pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control);
}
}
static int snd_acp_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
/*
* BIOS/firmware may re-enable MSI in PCI config space during
* system resume even though this driver only uses legacy INTx
* interrupts. If MSI is left enabled with stale address/data
* registers, the device will write interrupts to a bogus address
* causing IOMMU IO_PAGE_FAULT and interrupt delivery failure.
* Explicitly clear the MSI Enable bit before reinitializing
* the ACP hardware.
*/
acp_disable_msi_on_resume(pdev);
return acp_hw_resume(dev);
}
+5
View File
@@ -813,6 +813,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)
if (adau1372->use_pll) {
ret = adau1372_enable_pll(adau1372);
if (ret) {
if (!adau1372->pd_gpio)
regmap_update_bits(adau1372->regmap,
ADAU1372_REG_CLK_CTRL,
ADAU1372_CLK_CTRL_PLL_EN,
0);
regcache_cache_only(adau1372->regmap, true);
if (adau1372->pd_gpio)
gpiod_set_value(adau1372->pd_gpio, 1);
+6 -6
View File
@@ -284,22 +284,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261)
if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
/* set FORCE_PWM */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
/* set BOOST_OS_WIDTH */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
/* set BURST_LOOPR */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
/* set RSQN_DLY */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
/* set BURST_SSMODE */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
/* set BST_BURST */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
} else {
dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
}
+2 -1
View File
@@ -516,6 +516,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
* The regmap must remain in cache-only until the chip has
* booted, so use a bypassed read.
*/
val = 0;
ret = read_poll_timeout(regmap_read_bypassed, read_ret,
(val > 0) && (val < 0xffffffff),
CS35L56_HALO_STATE_POLL_US,
@@ -1227,7 +1228,7 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
return -EMSGSIZE;
ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count);
if (ret)
if (ret < 0)
return ret;
ret = cs35l56_stash_calibration(cs35l56_base, &cal_data);
+6 -1
View File
@@ -1488,6 +1488,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
int cs35l56_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "system_suspend\n");
@@ -1503,7 +1504,11 @@ int cs35l56_system_suspend(struct device *dev)
if (cs35l56->base.irq)
disable_irq(cs35l56->base.irq);
return pm_runtime_force_suspend(dev);
ret = pm_runtime_force_suspend(dev);
if ((ret < 0) && cs35l56->base.irq)
enable_irq(cs35l56->base.irq);
return ret;
}
EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
+3 -1
View File
@@ -912,12 +912,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
struct hdac_device *hdev = dev_to_hdac_dev(dev);
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_pcm *pcm;
const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
const char *cvt_name;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
if (ret < 0)
return ret;
cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
if (port == NULL)
return -EINVAL;
+6 -1
View File
@@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = {
.version = LPASS_CODEC_VERSION_1_0,
};
static const struct va_macro_data sc7280_va_data = {
.has_swr_master = false,
.has_npl_clk = false,
};
static const struct va_macro_data sm8450_va_data = {
.has_swr_master = true,
.has_npl_clk = true,
@@ -1757,7 +1762,7 @@ static const struct dev_pm_ops va_macro_pm_ops = {
};
static const struct of_device_id va_macro_dt_match[] = {
{ .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data },
{ .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data },
{ .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data },
{ .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data },
{ .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data },
+21 -14
View File
@@ -1592,6 +1592,7 @@ static void sma1307_check_fault_worker(struct work_struct *work)
struct sma1307_priv *sma1307 =
container_of(work, struct sma1307_priv, check_fault_work.work);
unsigned int status1_val, status2_val;
char volume[sizeof("VOLUME=0x12345678")];
char *envp[3] = { NULL, NULL, NULL };
if (sma1307->tsdw_cnt)
@@ -1607,7 +1608,7 @@ static void sma1307_check_fault_worker(struct work_struct *work)
if (~status1_val & SMA1307_OT1_OK_STATUS) {
dev_crit(sma1307->dev,
"%s: OT1(Over Temperature Level 1)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1");
envp[0] = "STATUS=OT1";
if (sma1307->sw_ot1_prot) {
/* Volume control (Current Volume -3dB) */
if ((sma1307->cur_vol + 6) <= 0xFA) {
@@ -1615,8 +1616,9 @@ static void sma1307_check_fault_worker(struct work_struct *work)
regmap_write(sma1307->regmap,
SMA1307_0A_SPK_VOL,
sma1307->cur_vol);
envp[1] = kasprintf(GFP_KERNEL,
"VOLUME=0x%02X", sma1307->cur_vol);
snprintf(volume, sizeof(volume),
"VOLUME=0x%02X", sma1307->cur_vol);
envp[1] = volume;
}
}
sma1307->tsdw_cnt++;
@@ -1625,48 +1627,53 @@ static void sma1307_check_fault_worker(struct work_struct *work)
SMA1307_0A_SPK_VOL, sma1307->init_vol);
sma1307->tsdw_cnt = 0;
sma1307->cur_vol = sma1307->init_vol;
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR");
envp[1] = kasprintf(GFP_KERNEL,
"VOLUME=0x%02X", sma1307->cur_vol);
envp[0] = "STATUS=OT1_CLEAR";
snprintf(volume, sizeof(volume), "VOLUME=0x%02X",
sma1307->cur_vol);
envp[1] = volume;
}
if (~status1_val & SMA1307_OT2_OK_STATUS) {
dev_crit(sma1307->dev,
"%s: OT2(Over Temperature Level 2)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2");
envp[0] = "STATUS=OT2";
envp[1] = NULL;
}
if (status1_val & SMA1307_UVLO_STATUS) {
dev_crit(sma1307->dev,
"%s: UVLO(Under Voltage Lock Out)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO");
envp[0] = "STATUS=UVLO";
envp[1] = NULL;
}
if (status1_val & SMA1307_OVP_BST_STATUS) {
dev_crit(sma1307->dev,
"%s: OVP_BST(Over Voltage Protection)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST");
envp[0] = "STATUS=OVP_BST";
envp[1] = NULL;
}
if (status2_val & SMA1307_OCP_SPK_STATUS) {
dev_crit(sma1307->dev,
"%s: OCP_SPK(Over Current Protect SPK)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK");
envp[0] = "STATUS=OCP_SPK";
envp[1] = NULL;
}
if (status2_val & SMA1307_OCP_BST_STATUS) {
dev_crit(sma1307->dev,
"%s: OCP_BST(Over Current Protect Boost)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST");
envp[0] = "STATUS=OCP_BST";
envp[1] = NULL;
}
if (status2_val & SMA1307_CLK_MON_STATUS) {
dev_crit(sma1307->dev,
"%s: CLK_FAULT(No clock input)\n", __func__);
envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT");
envp[0] = "STATUS=CLK_FAULT";
envp[1] = NULL;
}
if (envp[0] != NULL) {
if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp))
dev_err(sma1307->dev,
"%s: Error sending uevent\n", __func__);
kfree(envp[0]);
kfree(envp[1]);
}
if (sma1307->check_fault_status) {
+4 -3
View File
@@ -922,7 +922,8 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 1],
data[subblk_offset + 2]),
data[subblk_offset + 3]);
if (rc < 0) {
if (rc < 0 &&
!(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"process_block: single write error\n");
@@ -954,7 +955,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 1],
data[subblk_offset + 2]),
&(data[subblk_offset + 4]), len);
if (rc < 0) {
if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"%s: bulk_write error = %d\n",
@@ -992,7 +993,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 4]),
data[subblk_offset + 1],
data[subblk_offset + 5]);
if (rc < 0) {
if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"%s: update_bits error = %d\n",
+19 -6
View File
@@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
static const u8 dual_rate_q[] = {4, 8, 9, 12, 16};
struct snd_soc_component *component = dai->component;
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
bool dual_rate;
int clk;
int width = aic3x->slot_width;
@@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
/* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
dual_rate = params_rate(params) >= 64000;
/* Try to find a value for Q which allows us to bypass the PLL and
* generate CODEC_CLK directly. */
for (pll_q = 2; pll_q < 18; pll_q++)
if (aic3x->sysclk / (128 * pll_q) == fsref) {
bypass_pll = 1;
break;
if (dual_rate) {
for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) {
pll_q = dual_rate_q[i];
if (aic3x->sysclk / (128 * pll_q) == fsref) {
bypass_pll = 1;
break;
}
}
} else {
for (pll_q = 2; pll_q < 18; pll_q++)
if (aic3x->sysclk / (128 * pll_q) == fsref) {
bypass_pll = 1;
break;
}
}
if (bypass_pll) {
pll_q &= 0xf;
@@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
* right DAC to right channel input */
data = (LDAC2LCH | RDAC2RCH);
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
if (params_rate(params) >= 64000)
if (dual_rate)
data |= DUAL_RATE_MODE;
snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
data = (fsref * 20) / params_rate(params);
if (params_rate(params) < 64000)
if (!dual_rate)
data /= 2;
data /= 5;
data -= 2;
+20
View File
@@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
config_be.dst_addr_width = buswidth;
config_be.dst_maxburst = dma_params_be->maxburst;
/*
* For eDMA, the back-end may report a maxburst size that is not evenly
* divisible by the channel count. This causes the DMA transfer length
* to misalign with the FIFO boundary, resulting in wrong data and
* audible noise. Align maxburst to the nearest valid boundary:
* - If maxburst >= channel count, override to the channel count so
* each transfer equals exactly one audio frame.
* - If maxburst < channel count, override to 1 to avoid partial-frame
* transfers.
*/
if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) {
if (dma_params_be->maxburst >= params_channels(params)) {
config_be.src_maxburst = params_channels(params);
config_be.dst_maxburst = params_channels(params);
} else {
config_be.src_maxburst = 1;
config_be.dst_maxburst = 1;
}
}
memset(&audio_config, 0, sizeof(audio_config));
config_be.peripheral_config = &audio_config;
config_be.peripheral_size = sizeof(audio_config);
+6
View File
@@ -117,6 +117,9 @@ static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int reg_val, val, mix_clk;
if (item[0] >= e->items)
return -EINVAL;
/* Get current state */
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
@@ -157,6 +160,9 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
unsigned int reg_val, val, mask = 0, ctr = 0;
int ret;
if (item[0] >= e->items)
return -EINVAL;
/* Get current state */
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
+3
View File
@@ -36,6 +36,9 @@ static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int mux, changed;
if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
changed = snd_soc_component_test_bits(component, e->reg,
CTRL_DIN_LRCLK_SRC,
+3
View File
@@ -28,6 +28,9 @@ static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int mux, changed;
if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
changed = snd_soc_component_test_bits(component, e->reg,
CTRL_DATA_SEL,
+8
View File
@@ -549,6 +549,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
token = hdr->token & APM_WRITE_TOKEN_MASK;
done = data->payload;
if (!graph->rx_data.buf) {
mutex_unlock(&graph->lock);
break;
}
phys = graph->rx_data.buf[token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
@@ -571,6 +575,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
mutex_lock(&graph->lock);
rd_done = data->payload;
if (!graph->tx_data.buf) {
mutex_unlock(&graph->lock);
break;
}
phys = graph->tx_data.buf[hdr->token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
+3
View File
@@ -160,6 +160,9 @@ static int ge_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned int reg = e->reg;
int ret;
if (item[0] >= e->items)
return -EINVAL;
reg &= ~SDW_SDCA_CTL_CSEL(0x3F);
reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE);
+37 -12
View File
@@ -2905,20 +2905,18 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
{
struct snd_soc_dapm_widget *w;
struct snd_soc_dapm_widget *fallback = NULL;
char prefixed_pin[80];
const char *pin_name;
const char *prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",
prefix, pin);
pin_name = prefixed_pin;
} else {
pin_name = pin;
}
bool pin_has_prefix = snd_soc_dapm_pin_has_prefix(dapm->card, pin);
bool match;
for_each_card_widgets(dapm->card, w) {
if (!strcmp(w->name, pin_name)) {
match = false;
if (!strcmp(pin, w->name))
match = true;
else if (!pin_has_prefix && !snd_soc_dapm_widget_name_cmp(w, pin))
match = true;
if (match) {
if (w->dapm == dapm)
return w;
else
@@ -4873,6 +4871,33 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
/**
* snd_soc_dapm_pin_has_prefix - check if given pin has a known prefix
* @card: card to be checked
* @pin: pin name
*
* Returns true if given pin has a known prefix
*/
bool snd_soc_dapm_pin_has_prefix(struct snd_soc_card *card, const char *pin)
{
struct snd_soc_component *component;
const char *prefix;
size_t prefix_len;
for_each_card_components(card, component) {
prefix = component->name_prefix;
if (!prefix)
continue;
prefix_len = strlen(prefix);
if (!strncmp(pin, prefix, prefix_len) && pin[prefix_len] == ' ')
return true;
}
return false;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_pin_has_prefix);
/**
* snd_soc_dapm_free - free dapm resources
* @dapm: DAPM context
+34 -3
View File
@@ -1334,9 +1334,24 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
return ret;
}
static int soc_tplg_check_name(const char *name)
{
if (strnlen(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
return -EINVAL;
return 0;
}
static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream,
struct snd_soc_tplg_stream_caps *caps)
{
int ret;
ret = soc_tplg_check_name(caps->name);
if (ret)
return ret;
stream->stream_name = devm_kstrdup(tplg->dev, caps->name, GFP_KERNEL);
if (!stream->stream_name)
return -ENOMEM;
@@ -1388,7 +1403,11 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
if (dai_drv == NULL)
return -ENOMEM;
if (strlen(pcm->dai_name)) {
ret = soc_tplg_check_name(pcm->dai_name);
if (ret)
goto err;
if (pcm->dai_name[0]) {
dai_drv->name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL);
if (!dai_drv->name) {
ret = -ENOMEM;
@@ -1494,7 +1513,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
if (tplg->ops)
link->dobj.unload = tplg->ops->link_unload;
if (strlen(pcm->pcm_name)) {
ret = soc_tplg_check_name(pcm->pcm_name);
if (ret)
goto err;
if (pcm->pcm_name[0]) {
link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL);
link->stream_name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL);
if (!link->name || !link->stream_name) {
@@ -1504,7 +1527,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
}
link->id = le32_to_cpu(pcm->pcm_id);
if (strlen(pcm->dai_name)) {
ret = soc_tplg_check_name(pcm->dai_name);
if (ret)
goto err;
if (pcm->dai_name[0]) {
link->cpus->dai_name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL);
if (!link->cpus->dai_name) {
ret = -ENOMEM;
@@ -1856,6 +1883,10 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
memset(&dai_component, 0, sizeof(dai_component));
ret = soc_tplg_check_name(d->dai_name);
if (ret)
return ret;
dai_component.dai_name = d->dai_name;
dai = snd_soc_find_dai(&dai_component);
if (!dai) {
+3 -3
View File
@@ -266,10 +266,8 @@ config SND_SOC_SOF_METEORLAKE
config SND_SOC_SOF_INTEL_LNL
tristate
select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n
select SND_SOC_SOF_HDA_GENERIC
select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n
select SND_SOC_SOF_IPC4
select SND_SOC_SOF_INTEL_MTL
@@ -329,8 +327,10 @@ config SND_SOC_SOF_HDA_GENERIC
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
select SND_SOC_SOF_HDA_PROBES
select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE
select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE !=n
select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_LNL != n && \
SND_SOC_SOF_INTEL_SOUNDWIRE !=n
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
+60 -19
View File
@@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
if (data->size > scontrol->max_size - sizeof(*data)) {
if (data->size > scontrol->max_size - sizeof(*cdata) -
sizeof(*data)) {
dev_err_ratelimited(scomp->dev,
"%u bytes of control data is invalid, max is %zu\n",
data->size, scontrol->max_size - sizeof(*data));
data->size,
scontrol->max_size - sizeof(*cdata) -
sizeof(*data));
return -EINVAL;
}
@@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_abi_hdr *data = cdata->data;
const struct sof_abi_hdr *new_hdr =
(const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
@@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
if (data->size > scontrol->max_size - sizeof(*data)) {
dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n",
data->size, scontrol->max_size - sizeof(*data));
/* Validate the new data's size, not the old one */
if (new_hdr->size > scontrol->max_size - sizeof(*cdata) -
sizeof(*new_hdr)) {
dev_err_ratelimited(scomp->dev,
"data size too big %u bytes max is %zu\n",
new_hdr->size,
scontrol->max_size - sizeof(*cdata) -
sizeof(*new_hdr));
return -EINVAL;
}
size = data->size + sizeof(*data);
size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
@@ -389,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* be->max is coming from topology */
if (header.length > scontrol->max_size) {
dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n",
header.length, scontrol->max_size);
if (header.length > scontrol->max_size - sizeof(*cdata)) {
dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n",
header.length, scontrol->max_size - sizeof(*cdata));
return -EINVAL;
}
/* Ensure the data is large enough to contain the ABI header */
if (header.length < sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev,
"Bytes data size %u less than ABI header %zu\n",
header.length, sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -427,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
goto err_restore;
}
@@ -443,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
err_restore:
/* If we have an issue, we restore the old, valid bytes control data */
if (scontrol->old_ipc_control_data) {
memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size);
memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
}
@@ -482,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
}
/* check data size doesn't exceed max coming from topology */
if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
if (cdata->data->size > scontrol->max_size - sizeof(*cdata) -
sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev,
"User data size %u exceeds max size %zu\n",
cdata->data->size,
scontrol->max_size - sizeof(struct sof_abi_hdr));
scontrol->max_size - sizeof(*cdata) -
sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -535,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol,
return;
}
/* Verify the size fits within the allocation */
if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) -
sizeof(*local_cdata->data)) {
dev_err(scomp->dev,
"cdata binary size %u exceeds buffer\n",
cdata->num_elems);
return;
}
/* copy the new binary data */
memcpy(local_cdata->data, cdata->data, cdata->num_elems);
} else if (cdata->num_elems != scontrol->num_channels) {
@@ -626,16 +655,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_
return;
}
expected_size = sizeof(struct sof_ipc_ctrl_data);
switch (cdata->type) {
case SOF_CTRL_TYPE_VALUE_CHAN_GET:
case SOF_CTRL_TYPE_VALUE_CHAN_SET:
expected_size += cdata->num_elems *
sizeof(struct sof_ipc_ctrl_value_chan);
if (check_mul_overflow((size_t)cdata->num_elems,
sizeof(struct sof_ipc_ctrl_value_chan),
&expected_size))
return;
if (check_add_overflow(expected_size,
sizeof(struct sof_ipc_ctrl_data),
&expected_size))
return;
break;
case SOF_CTRL_TYPE_DATA_GET:
case SOF_CTRL_TYPE_DATA_SET:
expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
if (check_add_overflow((size_t)cdata->num_elems,
sizeof(struct sof_abi_hdr),
&expected_size))
return;
if (check_add_overflow(expected_size,
sizeof(struct sof_ipc_ctrl_data),
&expected_size))
return;
break;
default:
return;
+30 -4
View File
@@ -562,6 +562,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
const struct sof_abi_hdr *new_hdr =
(const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
int ret;
@@ -572,15 +574,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
if (data->size > scontrol->max_size - sizeof(*data)) {
/* Validate the new data's size, not the old one */
if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) {
dev_err_ratelimited(scomp->dev,
"data size too big %u bytes max is %zu\n",
data->size, scontrol->max_size - sizeof(*data));
new_hdr->size,
scontrol->max_size - sizeof(*new_hdr));
return -EINVAL;
}
size = data->size + sizeof(*data);
size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
@@ -880,6 +883,16 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
*/
if (type == SND_SOC_TPLG_TYPE_BYTES) {
struct sof_abi_hdr *data = cdata->data;
size_t source_size = struct_size(msg_data, data, msg_data->num_elems);
if (source_size > ndata->event_data_size) {
dev_warn(sdev->dev,
"%s: invalid bytes notification size for %s (%zu, %u)\n",
__func__, scontrol->name, source_size,
ndata->event_data_size);
scontrol->comp_data_dirty = true;
goto notify;
}
if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
dev_warn(sdev->dev,
@@ -892,6 +905,17 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
}
} else {
size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems);
if (source_size > ndata->event_data_size) {
dev_warn(sdev->dev,
"%s: invalid channel notification size for %s (%zu, %u)\n",
__func__, scontrol->name, source_size,
ndata->event_data_size);
scontrol->comp_data_dirty = true;
goto notify;
}
for (i = 0; i < msg_data->num_elems; i++) {
u32 channel = msg_data->chanv[i].channel;
@@ -919,6 +943,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
scontrol->comp_data_dirty = true;
}
notify:
/*
* Look up the ALSA kcontrol of the scontrol to be able to send a
* notification to user space
+6 -1
View File
@@ -733,10 +733,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
int ret;
while (array_size > 0 && total < count * token_instance_num) {
if (array_size < (int)sizeof(*array))
return -EINVAL;
asize = le32_to_cpu(array->size);
/* validate asize */
if (asize < sizeof(*array)) {
if (asize < (int)sizeof(*array)) {
dev_err(scomp->dev, "error: invalid array size 0x%x\n",
asize);
return -EINVAL;
@@ -2544,6 +2547,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
if (strstr(file, "dummy")) {
dev_err(scomp->dev,
"Function topology is required, please upgrade sof-firmware\n");
kfree(tplg_files);
return -EINVAL;
}
tplg_files[0] = file;
+3 -1
View File
@@ -62,13 +62,15 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { };
int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt);
unsigned int *item = uctl->value.enumerated.item;
unsigned int value = e->values[item[0]];
unsigned int value;
unsigned int i, bit_pos, reg_idx = 0, reg_val = 0;
int change = 0;
if (item[0] >= e->items)
return -EINVAL;
value = e->values[item[0]];
if (value) {
/* Get the register index and value to set */
reg_idx = (value - 1) / (8 * val_bytes);
+9 -1
View File
@@ -1794,8 +1794,16 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
/*
* skip empty packets. At least M-Audio's Fast Track Ultra stops
* streaming once it received a 0-byte OUT URB
*
* However, on devices where bytes==0 means every sync-source
* packet errored (e.g. Behringer Flow 8 returning -EXDEV bursts
* for entire capture URBs), an unconditional return starves the
* IFB-fed OUT ring permanently. Such devices set
* QUIRK_FLAG_IFB_SILENCE_ON_EMPTY to fall through and enqueue a
* packet_info with size 0 packets, so playback emits silence
* and the OUT ring keeps moving.
*/
if (bytes == 0)
if (bytes == 0 && !(ep->chip->quirk_flags & QUIRK_FLAG_IFB_SILENCE_ON_EMPTY))
return;
spin_lock_irqsave(&ep->lock, flags);
+2
View File
@@ -1083,6 +1083,8 @@ static int fcp_find_fc_interface(struct usb_mixer_interface *mixer)
if (desc->bInterfaceClass != 255)
continue;
if (desc->bNumEndpoints < 1)
continue;
epd = get_endpoint(intf->altsetting, 0);
private->bInterfaceNumber = desc->bInterfaceNumber;
+5
View File
@@ -470,6 +470,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi,
static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
{
list_del(&ep->list);
if (!ep->disconnected) {
ep->disconnected = 1;
kill_midi_urbs(ep, false);
drain_urb_queue(ep);
}
free_midi_urbs(ep);
kfree(ep);
}
+59 -42
View File
@@ -333,6 +333,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
int index = kcontrol->private_value & 0xff;
unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
unsigned long old_pval = kcontrol->private_value;
int err;
if (value > 1)
@@ -341,7 +342,11 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
return 0;
kcontrol->private_value = (value << 8) | index;
err = snd_audigy2nx_led_update(mixer, value, index);
return err < 0 ? err : 1;
if (err < 0) {
kcontrol->private_value = old_pval;
return err;
}
return 1;
}
static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
@@ -487,6 +492,7 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
unsigned int value = ucontrol->value.enumerated.item[0];
unsigned long old_pval = kcontrol->private_value;
int err;
if (value > 1)
@@ -497,7 +503,11 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value = value;
err = snd_emu0204_ch_switch_update(mixer, value);
return err < 0 ? err : 1;
if (err < 0) {
kcontrol->private_value = old_pval;
return err;
}
return 1;
}
static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
@@ -567,46 +577,30 @@ static bool snd_dualsense_ih_match(struct input_handler *handler,
{
struct dualsense_mixer_elem_info *mei;
struct usb_device *snd_dev;
char *input_dev_path, *usb_dev_path;
size_t usb_dev_path_len;
bool match = false;
struct device *parent;
mei = container_of(handler, struct dualsense_mixer_elem_info, ih);
snd_dev = mei->info.head.mixer->chip->dev;
input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
if (!input_dev_path) {
dev_warn(&snd_dev->dev, "Failed to get input dev path\n");
return false;
}
usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL);
if (!usb_dev_path) {
dev_warn(&snd_dev->dev, "Failed to get USB dev path\n");
goto free_paths;
}
/*
* Ensure the VID:PID matched input device supposedly owned by the
* hid-playstation driver belongs to the actual hardware handled by
* the current USB audio device, which implies input_dev_path being
* a subpath of usb_dev_path.
* the current USB audio device.
*
* This verification is necessary when there is more than one identical
* controller attached to the host system.
*
* The input device is registered below the HID device, USB interface and
* USB device, so compare the parent chain directly instead of building
* kobject path strings. This avoids dereferencing kobject names while the
* USB device hierarchy is being torn down during disconnect.
*/
usb_dev_path_len = strlen(usb_dev_path);
if (usb_dev_path_len >= strlen(input_dev_path))
goto free_paths;
for (parent = dev->dev.parent; parent; parent = parent->parent) {
if (parent == &snd_dev->dev)
return true;
}
usb_dev_path[usb_dev_path_len] = '/';
match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1);
free_paths:
kfree(input_dev_path);
kfree(usb_dev_path);
return match;
return false;
}
static int snd_dualsense_ih_connect(struct input_handler *handler,
@@ -821,7 +815,11 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value = new_status;
err = snd_xonar_u1_switch_update(list->mixer, new_status);
return err < 0 ? err : 1;
if (err < 0) {
kcontrol->private_value = old_status;
return err;
}
return 1;
}
static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
@@ -1159,7 +1157,8 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
u8 oldval = (kcontrol->private_value >> 24) & 0xff;
unsigned long old_pval = kcontrol->private_value;
u8 oldval = (old_pval >> 24) & 0xff;
u8 newval = ucontrol->value.integer.value[0];
int err;
@@ -1169,7 +1168,11 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value &= ~(0xff << 24);
kcontrol->private_value |= (unsigned int)newval << 24;
err = snd_ni_update_cur_val(list);
return err < 0 ? err : 1;
if (err < 0) {
kcontrol->private_value = old_pval;
return err;
}
return 1;
}
static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
@@ -1324,7 +1327,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
unsigned int pval = list->kctl->private_value;
unsigned long old_pval = list->kctl->private_value;
unsigned int pval = old_pval;
int cur_val, err, new_val;
cur_val = pval >> 24;
@@ -1335,7 +1339,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
kctl->private_value &= ~(0xff << 24);
kctl->private_value |= new_val << 24;
err = snd_ftu_eff_switch_update(list);
return err < 0 ? err : 1;
if (err < 0) {
kctl->private_value = old_pval;
return err;
}
return 1;
}
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@@ -2114,13 +2122,18 @@ static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
unsigned char value = !!ucontrol->value.integer.value[0];
unsigned long old_pval = kcontrol->private_value;
int err;
if (kcontrol->private_value == value)
return 0;
kcontrol->private_value = value;
err = snd_soundblaster_e1_switch_update(list->mixer, value);
return err < 0 ? err : 1;
if (err < 0) {
kcontrol->private_value = old_pval;
return err;
}
return 1;
}
static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)
@@ -2998,12 +3011,14 @@ static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
if (val == old_value)
return 0;
err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
if (err < 0)
return err;
kcontrol->private_value = reg
| ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
| ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
return err < 0 ? err : 1;
return 1;
}
static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
@@ -3188,11 +3203,13 @@ static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
err = snd_bbfpro_vol_update(mixer, idx, new_val);
if (err < 0)
return err;
kcontrol->private_value = idx
| (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
err = snd_bbfpro_vol_update(mixer, idx, new_val);
return err < 0 ? err : 1;
return 1;
}
static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
+3 -1
View File
@@ -680,7 +680,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl,
val = ucontrol->value.integer.value[0];
val = val + opt->start;
if (val != oval) {
snd_usb_set_cur_mix_value(elem, 0, 0, val);
err = snd_usb_set_cur_mix_value(elem, 0, 0, val);
if (err < 0)
return err;
return 1;
}
return 0;
+179 -22
View File
@@ -612,6 +612,20 @@ struct scarlett2_config_set {
const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
};
/* Map firmware versions to config sets per-device.
*
* Each device lists one or more entries, sorted in ascending order of
* from_firmware_version. At probe time the running firmware version
* is looked up against this list and the last entry whose
* from_firmware_version is <= the running version is selected.
*
* The list is terminated by a sentinel entry with config_set == NULL.
*/
struct scarlett2_config_set_entry {
u16 from_firmware_version;
const struct scarlett2_config_set *config_set;
};
/* Input gain TLV dB ranges */
static const DECLARE_TLV_DB_MINMAX(
@@ -923,6 +937,63 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
}
};
/* 2i2 Gen 4, firmware version 2417 and above
*
* Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes; all other
* offsets are unchanged from scarlett2_config_set_gen4_2i2.
*/
static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = {
.notifications = scarlett4_2i2_notifications,
.param_buf_addr = 0xfc,
.input_gain_tlv = db_scale_gen4_gain,
.autogain_status_texts = scarlett2_autogain_status_gen4,
.items = {
[SCARLETT2_CONFIG_MSD_SWITCH] = {
.offset = 0x49, .size = 8, .activate = 4 },
[SCARLETT2_CONFIG_DIRECT_MONITOR] = {
.offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 },
[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
.offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 },
[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
.offset = 0x137, .size = 8 },
[SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
.offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 },
[SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
.offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 },
[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
.offset = 0x48, .size = 8, .activate = 11, .pbuf = 1,
.mute = 1 },
[SCARLETT2_CONFIG_INPUT_GAIN] = {
.offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
.offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1,
.mute = 1 },
[SCARLETT2_CONFIG_SAFE_SWITCH] = {
.offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 },
[SCARLETT2_CONFIG_AIR_SWITCH] = {
.offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
.offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
.offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 },
[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
.offset = 0x2a4, .size = 16, .activate = 36 }
}
};
/* 4i4 Gen 4 */
static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
.notifications = scarlett4_4i4_notifications,
@@ -1100,8 +1171,8 @@ struct scarlett2_meter_entry {
};
struct scarlett2_device_info {
/* which set of configuration parameters the device uses */
const struct scarlett2_config_set *config_set;
/* which sets of configuration parameters the device uses */
const struct scarlett2_config_set_entry *config_sets;
/* minimum firmware version required */
u16 min_firmware_version;
@@ -1343,7 +1414,10 @@ struct scarlett2_data {
/*** Model-specific data ***/
static const struct scarlett2_device_info s6i6_gen2_info = {
.config_set = &scarlett2_config_set_gen2a,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen2a },
{ }
},
.level_input_count = 2,
.pad_input_count = 2,
@@ -1393,7 +1467,10 @@ static const struct scarlett2_device_info s6i6_gen2_info = {
};
static const struct scarlett2_device_info s18i8_gen2_info = {
.config_set = &scarlett2_config_set_gen2a,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen2a },
{ }
},
.level_input_count = 2,
.pad_input_count = 4,
@@ -1446,7 +1523,10 @@ static const struct scarlett2_device_info s18i8_gen2_info = {
};
static const struct scarlett2_device_info s18i20_gen2_info = {
.config_set = &scarlett2_config_set_gen2b,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen2b },
{ }
},
.line_out_descrs = {
"Monitor L",
@@ -1503,7 +1583,10 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
};
static const struct scarlett2_device_info solo_gen3_info = {
.config_set = &scarlett2_config_set_gen3a,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3a },
{ }
},
.level_input_count = 1,
.level_input_first = 1,
.air_input_count = 1,
@@ -1513,7 +1596,10 @@ static const struct scarlett2_device_info solo_gen3_info = {
};
static const struct scarlett2_device_info s2i2_gen3_info = {
.config_set = &scarlett2_config_set_gen3a,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3a },
{ }
},
.level_input_count = 2,
.air_input_count = 2,
.phantom_count = 1,
@@ -1522,7 +1608,10 @@ static const struct scarlett2_device_info s2i2_gen3_info = {
};
static const struct scarlett2_device_info s4i4_gen3_info = {
.config_set = &scarlett2_config_set_gen3b,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3b },
{ }
},
.level_input_count = 2,
.pad_input_count = 2,
.air_input_count = 2,
@@ -1571,7 +1660,10 @@ static const struct scarlett2_device_info s4i4_gen3_info = {
};
static const struct scarlett2_device_info s8i6_gen3_info = {
.config_set = &scarlett2_config_set_gen3b,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3b },
{ }
},
.level_input_count = 2,
.pad_input_count = 2,
.air_input_count = 2,
@@ -1637,7 +1729,10 @@ static const char * const scarlett2_spdif_s18i8_gen3_texts[] = {
};
static const struct scarlett2_device_info s18i8_gen3_info = {
.config_set = &scarlett2_config_set_gen3c,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3c },
{ }
},
.has_speaker_switching = 1,
.level_input_count = 2,
.pad_input_count = 4,
@@ -1729,7 +1824,10 @@ static const char * const scarlett2_spdif_s18i20_gen3_texts[] = {
};
static const struct scarlett2_device_info s18i20_gen3_info = {
.config_set = &scarlett2_config_set_gen3c,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_gen3c },
{ }
},
.has_speaker_switching = 1,
.has_talkback = 1,
.level_input_count = 2,
@@ -1803,7 +1901,10 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
};
static const struct scarlett2_device_info vocaster_one_info = {
.config_set = &scarlett2_config_set_vocaster,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 1769, &scarlett2_config_set_vocaster },
{ }
},
.min_firmware_version = 1769,
.has_devmap = 1,
@@ -1847,7 +1948,10 @@ static const struct scarlett2_device_info vocaster_one_info = {
};
static const struct scarlett2_device_info vocaster_two_info = {
.config_set = &scarlett2_config_set_vocaster,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 1769, &scarlett2_config_set_vocaster },
{ }
},
.min_firmware_version = 1769,
.has_devmap = 1,
@@ -1892,7 +1996,10 @@ static const struct scarlett2_device_info vocaster_two_info = {
};
static const struct scarlett2_device_info solo_gen4_info = {
.config_set = &scarlett2_config_set_gen4_solo,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 2115, &scarlett2_config_set_gen4_solo },
{ }
},
.min_firmware_version = 2115,
.has_devmap = 1,
@@ -1947,7 +2054,11 @@ static const struct scarlett2_device_info solo_gen4_info = {
};
static const struct scarlett2_device_info s2i2_gen4_info = {
.config_set = &scarlett2_config_set_gen4_2i2,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 2115, &scarlett2_config_set_gen4_2i2 },
{ 2417, &scarlett2_config_set_gen4_2i2_2417 },
{ }
},
.min_firmware_version = 2115,
.has_devmap = 1,
@@ -2002,7 +2113,10 @@ static const struct scarlett2_device_info s2i2_gen4_info = {
};
static const struct scarlett2_device_info s4i4_gen4_info = {
.config_set = &scarlett2_config_set_gen4_4i4,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 2089, &scarlett2_config_set_gen4_4i4 },
{ }
},
.min_firmware_version = 2089,
.has_devmap = 1,
@@ -2051,7 +2165,10 @@ static const struct scarlett2_device_info s4i4_gen4_info = {
};
static const struct scarlett2_device_info clarett_2pre_info = {
.config_set = &scarlett2_config_set_clarett,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_clarett },
{ }
},
.level_input_count = 2,
.air_input_count = 2,
@@ -2107,7 +2224,10 @@ static const char * const scarlett2_spdif_clarett_texts[] = {
};
static const struct scarlett2_device_info clarett_4pre_info = {
.config_set = &scarlett2_config_set_clarett,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_clarett },
{ }
},
.level_input_count = 2,
.air_input_count = 4,
@@ -2163,7 +2283,10 @@ static const struct scarlett2_device_info clarett_4pre_info = {
};
static const struct scarlett2_device_info clarett_8pre_info = {
.config_set = &scarlett2_config_set_clarett,
.config_sets = (const struct scarlett2_config_set_entry[]) {
{ 0, &scarlett2_config_set_clarett },
{ }
},
.level_input_count = 2,
.air_input_count = 8,
@@ -8211,10 +8334,32 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
/*** Initialisation ***/
/* Select the config_set matching the running firmware version.
*
* The device info's config_sets array is ordered by ascending
* from_firmware_version; pick the last entry whose version is <= the
* running firmware version. If the running firmware is older than the
* first entry's from_firmware_version (i.e. older than the driver's
* minimum supported version for this device), the first entry's
* config_set is selected anyway so firmware updates can still be done
* (requires only the ACK handler), but the usual mixer controls
* aren't created.
*/
static void scarlett2_resolve_config_set(struct scarlett2_data *private)
{
const struct scarlett2_config_set_entry *entry =
private->info->config_sets;
private->config_set = entry->config_set;
for (entry++; entry->config_set; entry++)
if (entry->from_firmware_version <= private->firmware_version)
private->config_set = entry->config_set;
}
static void scarlett2_count_io(struct scarlett2_data *private)
{
const struct scarlett2_device_info *info = private->info;
const struct scarlett2_config_set *config_set = info->config_set;
const struct scarlett2_config_set *config_set = private->config_set;
const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
int port_type, srcs = 0, dsts = 0, i;
@@ -8311,9 +8456,14 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer,
mixer->private_suspend = scarlett2_private_suspend;
private->info = entry->info;
private->config_set = entry->info->config_set;
/* Set config_set to the first entry's config_set so the
* notify handler has a valid pointer while USB init runs; it
* is re-resolved once the firmware version has been read.
*/
private->config_set = entry->info->config_sets[0].config_set;
private->series_name = entry->series_name;
scarlett2_count_io(private);
private->scarlett2_seq = 0;
private->mixer = mixer;
@@ -8717,6 +8867,13 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
/* Now that the firmware version is known, pick the matching
* config_set
*/
scarlett2_resolve_config_set(private);
scarlett2_count_io(private);
/* Get the upgrade & settings flash segment numbers */
err = scarlett2_get_flash_segment_nums(mixer);
if (err < 0)
+76 -51
View File
@@ -224,14 +224,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg));
if (err > 0) {
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err);
return err;
}
return err > 0 ? 1 : 0;
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol,
@@ -283,14 +283,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out));
if (err > 0) {
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set master, err:%d\n", err);
return err;
}
return err > 0 ? 1 : 0;
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
@@ -324,14 +324,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
break;
}
if (err > 0) {
elem->cached |= 1;
elem->cache_val[0] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err);
return err;
}
return err > 0 ? 1 : 0;
elem->cached |= 1;
elem->cache_val[0] = val;
return 1;
}
static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
@@ -392,14 +392,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in));
if (err > 0) {
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err);
return err;
}
return err > 0 ? 1 : 0;
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol,
@@ -435,6 +435,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
int index = ucontrol->id.index;
char buf[sizeof(comp_msg)];
int val_idx, val;
int threshold, ratio, attack, release, gain, switch_on;
int err;
val = ucontrol->value.integer.value[0];
@@ -447,36 +448,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
/* new control value incl. bias*/
val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE;
store->val[val_idx][index] = ucontrol->value.integer.value[0];
threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)]
[index];
ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index];
attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index];
release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)]
[index];
gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)]
[index];
switch (val_idx) {
case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD):
threshold = val;
break;
case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO):
ratio = val;
break;
case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK):
attack = val;
break;
case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE):
release = val;
break;
case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN):
gain = val;
break;
case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH):
switch_on = val;
break;
}
/* prepare compressor URB message from template */
memcpy(buf, comp_msg, sizeof(comp_msg));
/* place comp values in message buffer watch bias! */
buf[8] = store->val[
COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index]
- SND_US16X08_COMP_THRESHOLD_BIAS;
buf[11] = ratio_map[store->val[
COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]];
buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]
+ SND_US16X08_COMP_ATTACK_BIAS;
buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index]
+ SND_US16X08_COMP_RELEASE_BIAS;
buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index];
buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS;
buf[11] = ratio_map[ratio];
buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS;
buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS;
buf[20] = gain;
buf[26] = switch_on;
/* place channel selector in message buffer */
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg));
if (err > 0) {
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err);
return err;
}
store->val[val_idx][index] = val;
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
@@ -529,13 +555,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol,
msleep(15);
}
if (err > 0) {
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err);
return err;
}
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
@@ -578,11 +604,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
/* copy URB buffer from EQ template */
memcpy(buf, eqs_msq, sizeof(eqs_msq));
store->val[b_idx][p_idx][index] = val;
buf[20] = store->val[b_idx][3][index];
buf[17] = store->val[b_idx][2][index];
buf[14] = store->val[b_idx][1][index];
buf[11] = store->val[b_idx][0][index];
buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index];
buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index];
buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index];
buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index];
/* place channel index in URB buffer */
buf[5] = index + 1;
@@ -592,14 +617,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq));
if (err > 0) {
/* store new value in EQ band cache */
elem->cached |= 1 << index;
elem->cache_val[index] = val;
} else {
if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err);
return err;
}
store->val[b_idx][p_idx][index] = val;
/* store new value in EQ band cache */
elem->cached |= 1 << index;
elem->cache_val[index] = val;
return 1;
}
@@ -1418,4 +1444,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
return 0;
}
+1 -1
View File
@@ -113,7 +113,7 @@ int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev)
struct snd_usb_substream *subs;
struct snd_usb_stream *as;
char ctl_name[48];
int ret;
int ret = 0;
list_for_each_entry(as, &chip->pcm_list, list) {
subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
+25 -5
View File
@@ -794,15 +794,23 @@ static void qmi_stop_session(void)
continue;
}
/* Release XHCI endpoints */
if (info->data_ep_pipe)
if (info->data_ep_pipe) {
ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
info->data_ep_pipe);
xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
if (ep)
xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
ep);
info->data_ep_pipe = 0;
}
if (info->sync_ep_pipe)
if (info->sync_ep_pipe) {
ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
info->sync_ep_pipe);
xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
if (ep)
xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
ep);
info->sync_ep_pipe = 0;
}
disable_audio_stream(subs);
}
@@ -1157,6 +1165,7 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs,
tr_pa = page_to_phys(pg);
mem_info->dma = sg_dma_address(sgt->sgl);
sg_free_table(sgt);
kfree(sgt);
/* data transfer ring */
iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa,
@@ -1226,6 +1235,7 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
er_pa = page_to_phys(pg);
mem_info->dma = sg_dma_address(sgt->sgl);
sg_free_table(sgt);
kfree(sgt);
iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa,
PAGE_SIZE);
@@ -1616,8 +1626,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
if (req_msg->service_interval_valid) {
ret = get_data_interval_from_si(subs,
req_msg->service_interval);
if (ret == -EINVAL)
if (ret == -EINVAL) {
if (req_msg->enable) {
guard(mutex)(&chip->mutex);
subs->opened = 0;
}
goto response;
}
datainterval = ret;
}
@@ -1638,6 +1653,11 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
subs->opened = 0;
}
} else {
if (info_idx < 0) {
ret = -EINVAL;
goto response;
}
info = &uadev[pcm_card_num].info[info_idx];
if (info->data_ep_pipe) {
ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
+3
View File
@@ -2365,6 +2365,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x1397, 0x0509, /* Behringer UMC404HD */
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */
QUIRK_FLAG_IFB_SILENCE_ON_EMPTY),
DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */
@@ -2606,6 +2608,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP),
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY),
NULL
};
+8
View File
@@ -236,6 +236,12 @@ extern bool snd_usb_skip_validation;
* QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL
* Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams.
* Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE
* QUIRK_FLAG_IFB_SILENCE_ON_EMPTY
* In implicit feedback mode, when an entire capture URB returns with
* all iso_frame_desc[i].status != 0 (bytes==0), do not silently return
* from snd_usb_handle_sync_urb. Instead fall through and enqueue a
* packet_info containing only size-0 packets, so the OUT ring keeps
* moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
*/
enum {
@@ -268,6 +274,7 @@ enum {
QUIRK_TYPE_SKIP_IFACE_SETUP = 26,
QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27,
QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29,
/* Please also edit snd_usb_audio_quirk_flag_names */
};
@@ -302,5 +309,6 @@ enum {
#define QUIRK_FLAG_SKIP_IFACE_SETUP QUIRK_FLAG(SKIP_IFACE_SETUP)
#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
#define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY)
#endif /* __USBAUDIO_H */
+12 -5
View File
@@ -383,7 +383,7 @@ static int alsa_open(struct snd_pcm_substream *substream)
stream_clear(stream);
xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->req, true);
ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
alsa_hw_rule, stream,
@@ -504,6 +504,8 @@ static int alsa_hw_free(struct snd_pcm_substream *substream)
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
int ret;
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, false);
ret = xen_snd_front_stream_close(&stream->evt_pair->req);
stream_free(stream);
return ret;
@@ -538,6 +540,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream)
return ret;
stream->is_open = true;
xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, true);
}
return 0;
@@ -577,20 +580,24 @@ void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
{
struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
if (!runtime->buffer_size || !runtime->period_size)
return;
cur_frame = bytes_to_frames(runtime, pos_bytes);
delta = cur_frame - stream->be_cur_frame;
stream->be_cur_frame = cur_frame;
new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
new_hw_ptr = (new_hw_ptr + delta) % runtime->buffer_size;
atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
stream->out_frames += delta;
if (stream->out_frames > substream->runtime->period_size) {
stream->out_frames %= substream->runtime->period_size;
if (stream->out_frames > runtime->period_size) {
stream->out_frames %= runtime->period_size;
snd_pcm_period_elapsed(substream);
}
}
+19 -9
View File
@@ -94,6 +94,9 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
guard(mutex)(&channel->ring_io_lock);
if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
return IRQ_HANDLED;
prod = page->in_prod;
/* Ensure we see ring contents up to prod. */
virt_rmb();
@@ -455,8 +458,8 @@ fail_to_end:
return ret;
}
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected)
void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel,
bool is_connected)
{
enum xen_snd_front_evtchnl_state state;
@@ -465,13 +468,16 @@ void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair
else
state = EVTCHNL_STATE_DISCONNECTED;
scoped_guard(mutex, &evt_pair->req.ring_io_lock) {
evt_pair->req.state = state;
scoped_guard(mutex, &channel->ring_io_lock) {
channel->state = state;
}
}
scoped_guard(mutex, &evt_pair->evt.ring_io_lock) {
evt_pair->evt.state = state;
}
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected)
{
xen_snd_front_evtchnl_set_connected(&evt_pair->req, is_connected);
xen_snd_front_evtchnl_set_connected(&evt_pair->evt, is_connected);
}
void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair)
@@ -481,7 +487,11 @@ void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pai
}
scoped_guard(mutex, &evt_pair->evt.ring_io_lock) {
evt_pair->evt.evt_next_id = 0;
evt_pair->evt.evt_id = 0;
/* Drop obsolete events queued for the previous stream instance. */
evt_pair->evt.u.evt.page->in_cons =
evt_pair->evt.u.evt.page->in_prod;
/* Ensure the consumer index is visible before stream reuse. */
virt_wmb();
}
}
+4 -2
View File
@@ -37,9 +37,9 @@ struct xen_snd_front_evtchnl {
/* State of the event channel. */
enum xen_snd_front_evtchnl_state state;
enum xen_snd_front_evtchnl_type type;
/* Either response id or incoming event id. */
/* Current response id or next expected incoming event id. */
u16 evt_id;
/* Next request id or next expected event id. */
/* Next request id. */
u16 evt_next_id;
/* Shared ring access lock. */
struct mutex ring_io_lock;
@@ -77,6 +77,8 @@ void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info);
int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info);
void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl);
void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel,
bool is_connected);
void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair,
bool is_connected);