mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9.git
synced 2026-09-09 00:08:12 +08:00
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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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, ¶ms);
|
||||
err = snd_rawmidi_output_params(rfile.output, ¶ms);
|
||||
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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user