aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/sound/pcm.h16
-rw-r--r--include/uapi/sound/asequencer.h7
-rw-r--r--include/uapi/sound/asound.h9
-rw-r--r--sound/core/control.c9
-rw-r--r--sound/core/pcm_dmaengine.c30
-rw-r--r--sound/core/pcm_lib.c52
-rw-r--r--sound/core/pcm_native.c8
-rw-r--r--sound/core/seq/seq_clientmgr.c4
-rw-r--r--sound/core/seq/seq_queue.c6
-rw-r--r--sound/core/seq/seq_timer.c21
-rw-r--r--sound/core/seq/seq_timer.h4
-rw-r--r--sound/core/seq/seq_ump_convert.c12
-rw-r--r--sound/core/vmaster.c8
-rw-r--r--sound/hda/hdac_device.c1
-rw-r--r--sound/hda/hdmi_chmap.c18
-rw-r--r--sound/hda/intel-dsp-config.c11
-rw-r--r--sound/isa/sb/emu8000.c6
-rw-r--r--sound/isa/sb/sb16_csp.c12
-rw-r--r--sound/oss/dmasound/dmasound_core.c1
-rw-r--r--sound/pci/emu10k1/p16v.c17
-rw-r--r--sound/pci/hda/Kconfig13
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/cs35l41_hda.c135
-rw-r--r--sound/pci/hda/cs35l41_hda.h1
-rw-r--r--sound/pci/hda/cs35l41_hda_property.c23
-rw-r--r--sound/pci/hda/cs35l56_hda.c124
-rw-r--r--sound/pci/hda/cs35l56_hda.h3
-rw-r--r--sound/pci/hda/hda_codec.c23
-rw-r--r--sound/pci/hda/hda_component.c75
-rw-r--r--sound/pci/hda/hda_component.h48
-rw-r--r--sound/pci/hda/hda_cs_dsp_ctl.c2
-rw-r--r--sound/pci/hda/hda_intel.c2
-rw-r--r--sound/pci/hda/patch_realtek.c84
-rw-r--r--sound/pci/hda/patch_senarytech.c244
-rw-r--r--sound/pci/hda/tas2781_hda_i2c.c50
-rw-r--r--sound/ppc/keywest.c4
-rw-r--r--sound/spi/at73c213.c8
-rw-r--r--sound/usb/format.c17
-rw-r--r--sound/usb/mixer.c45
-rw-r--r--sound/usb/mixer_quirks.c20
-rw-r--r--sound/xen/xen_snd_front_alsa.c5
-rw-r--r--tools/testing/selftests/alsa/mixer-test.c45
-rw-r--r--tools/testing/selftests/alsa/pcm-test.c2
45 files changed, 924 insertions, 310 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 93f8f2ed3cb2..33fa456699f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24959,6 +24959,12 @@ F: mm/zpool.c
F: mm/zswap.c
F: tools/testing/selftests/cgroup/test_zswap.c
+SENARYTECH AUDIO CODEC DRIVER
+M: bo liu <[email protected]>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+F: sound/pci/hda/patch_senarytech.c
+
THE REST
M: Linus Torvalds <[email protected]>
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 942a587bb97e..0168c6a60148 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -3112,6 +3112,7 @@
#define PCI_DEVICE_ID_INTEL_HDA_LNL_P 0xa828
#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152
#define PCI_DEVICE_ID_INTEL_HDA_BMG 0xe2f7
+#define PCI_DEVICE_ID_INTEL_HDA_PTL 0xe428
#define PCI_DEVICE_ID_INTEL_HDA_CML_R 0xf0c8
#define PCI_DEVICE_ID_INTEL_HDA_RKL_S 0xf1c8
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 3edd7a7346da..ac8f3aef9205 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -93,6 +93,7 @@ struct snd_pcm_ops {
#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2
/* 3 is absent slot. */
#define SNDRV_PCM_IOCTL1_FIFO_SIZE 4
+#define SNDRV_PCM_IOCTL1_SYNC_ID 5
#define SNDRV_PCM_TRIGGER_STOP 0
#define SNDRV_PCM_TRIGGER_START 1
@@ -401,7 +402,7 @@ struct snd_pcm_runtime {
snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
snd_pcm_uframes_t silence_filled; /* already filled part of silence area */
- union snd_pcm_sync_id sync; /* hardware synchronization ID */
+ bool std_sync_id; /* hardware synchronization - standard per card ID */
/* -- mmap -- */
struct snd_pcm_mmap_status *status;
@@ -1155,7 +1156,18 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int
void snd_pcm_set_ops(struct snd_pcm * pcm, int direction,
const struct snd_pcm_ops *ops);
-void snd_pcm_set_sync(struct snd_pcm_substream *substream);
+void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+ const unsigned char *id, unsigned int len);
+/**
+ * snd_pcm_set_sync - set the PCM sync id
+ * @substream: the pcm substream
+ *
+ * Use the default PCM sync identifier for the specific card.
+ */
+static inline void snd_pcm_set_sync(struct snd_pcm_substream *substream)
+{
+ substream->runtime->std_sync_id = true;
+}
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg);
void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream);
diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h
index c85fdd8895d8..39b37edcf813 100644
--- a/include/uapi/sound/asequencer.h
+++ b/include/uapi/sound/asequencer.h
@@ -10,7 +10,7 @@
#include <sound/asound.h>
/** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4)
/**
* definition of sequencer event types
@@ -523,11 +523,12 @@ struct snd_seq_queue_status {
/* queue tempo */
struct snd_seq_queue_tempo {
int queue; /* sequencer queue */
- unsigned int tempo; /* current tempo, us/tick */
+ unsigned int tempo; /* current tempo, us/tick (or different time-base below) */
int ppq; /* time resolution, ticks/quarter */
unsigned int skew_value; /* queue skew */
unsigned int skew_base; /* queue skew base */
- char reserved[24]; /* for the future */
+ unsigned short tempo_base; /* tempo base in nsec unit; either 10 or 1000 */
+ char reserved[22]; /* for the future */
};
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 628d46a0da92..8bf7e8a0eb6f 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -142,7 +142,7 @@ struct snd_hwdep_dsp_image {
* *
*****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 17)
+#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 18)
typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t;
@@ -334,7 +334,7 @@ union snd_pcm_sync_id {
unsigned char id[16];
unsigned short id16[8];
unsigned int id32[4];
-};
+} __attribute__((deprecated));
struct snd_pcm_info {
unsigned int device; /* RO/WR (control): device number */
@@ -348,7 +348,7 @@ struct snd_pcm_info {
int dev_subclass; /* SNDRV_PCM_SUBCLASS_* */
unsigned int subdevices_count;
unsigned int subdevices_avail;
- union snd_pcm_sync_id sync; /* hardware synchronization ID */
+ unsigned char pad1[16]; /* was: hardware synchronization ID */
unsigned char reserved[64]; /* reserved for future... */
};
@@ -420,7 +420,8 @@ struct snd_pcm_hw_params {
unsigned int rate_num; /* R: rate numerator */
unsigned int rate_den; /* R: rate denominator */
snd_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */
- unsigned char reserved[64]; /* reserved for future */
+ unsigned char sync[16]; /* R: synchronization ID (perfect sync - one clock source) */
+ unsigned char reserved[48]; /* reserved for future */
};
enum {
diff --git a/sound/core/control.c b/sound/core/control.c
index fb0c60044f7b..f64a555f404f 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -604,6 +604,7 @@ static inline int snd_ctl_remove_locked(struct snd_card *card,
*
* Removes the control from the card and then releases the instance.
* You don't need to call snd_ctl_free_one().
+ * Passing NULL to @kcontrol argument is allowed as noop.
*
* Return: 0 if successful, or a negative error code on failure.
*
@@ -611,6 +612,8 @@ static inline int snd_ctl_remove_locked(struct snd_card *card,
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
+ if (!kcontrol)
+ return 0;
guard(rwsem_write)(&card->controls_rwsem);
return snd_ctl_remove_locked(card, kcontrol);
}
@@ -1480,12 +1483,16 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int change;
+ int err, change;
struct user_element *ue = kcontrol->private_data;
unsigned int size = ue->elem_data_size;
char *dst = ue->elem_data +
snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
+ err = sanity_check_input_values(ue->card, ucontrol, &ue->info, false);
+ if (err < 0)
+ return err;
+
change = memcmp(&ucontrol->value, dst, size) != 0;
if (change)
memcpy(dst, &ucontrol->value, size);
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index ed07fa5693d2..b54336d4bffc 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -359,6 +359,23 @@ int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
+static void __snd_dmaengine_pcm_close(struct snd_pcm_substream *substream,
+ bool release_channel)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_PAUSED)
+ dmaengine_terminate_async(prtd->dma_chan);
+
+ dmaengine_synchronize(prtd->dma_chan);
+ if (release_channel)
+ dma_release_channel(prtd->dma_chan);
+ kfree(prtd);
+}
+
/**
* snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
* @substream: PCM substream
@@ -367,11 +384,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
*/
int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
{
- struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
-
- dmaengine_synchronize(prtd->dma_chan);
- kfree(prtd);
-
+ __snd_dmaengine_pcm_close(substream, false);
return 0;
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
@@ -387,12 +400,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
*/
int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
{
- struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
-
- dmaengine_synchronize(prtd->dma_chan);
- dma_release_channel(prtd->dma_chan);
- kfree(prtd);
-
+ __snd_dmaengine_pcm_close(substream, true);
return 0;
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 6f73b3c2c205..6e7905749c4a 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -516,21 +516,38 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
EXPORT_SYMBOL(snd_pcm_set_ops);
/**
- * snd_pcm_set_sync - set the PCM sync id
+ * snd_pcm_set_sync_per_card - set the PCM sync id with card number
* @substream: the pcm substream
+ * @params: modified hardware parameters
+ * @id: identifier (max 12 bytes)
+ * @len: identifier length (max 12 bytes)
*
- * Sets the PCM sync identifier for the card.
+ * Sets the PCM sync identifier for the card with zero padding.
+ *
+ * User space or any user should use this 16-byte identifier for a comparison only
+ * to check if two IDs are similar or different. Special case is the identifier
+ * containing only zeros. Interpretation for this combination is - empty (not set).
+ * The contents of the identifier should not be interpreted in any other way.
+ *
+ * The synchronization ID must be unique per clock source (usually one sound card,
+ * but multiple soundcard may use one PCM word clock source which means that they
+ * are fully synchronized).
+ *
+ * This routine composes this ID using card number in first four bytes and
+ * 12-byte additional ID. When other ID composition is used (e.g. for multiple
+ * sound cards), make sure that the composition does not clash with this
+ * composition scheme.
*/
-void snd_pcm_set_sync(struct snd_pcm_substream *substream)
+void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ const unsigned char *id, unsigned int len)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->sync.id32[0] = substream->pcm->card->number;
- runtime->sync.id32[1] = -1;
- runtime->sync.id32[2] = -1;
- runtime->sync.id32[3] = -1;
+ *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number);
+ len = min(12, len);
+ memcpy(params->sync + 4, id, len);
+ memset(params->sync + 4 + len, 0, 12 - len);
}
-EXPORT_SYMBOL(snd_pcm_set_sync);
+EXPORT_SYMBOL_GPL(snd_pcm_set_sync_per_card);
/*
* Standard ioctl routine
@@ -1810,6 +1827,18 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
return 0;
}
+static int snd_pcm_lib_ioctl_sync_id(struct snd_pcm_substream *substream,
+ void *arg)
+{
+ static const unsigned char id[12] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff };
+
+ if (substream->runtime->std_sync_id)
+ snd_pcm_set_sync_per_card(substream, arg, id, sizeof(id));
+ return 0;
+}
+
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance
@@ -1831,6 +1860,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
return snd_pcm_lib_ioctl_channel_info(substream, arg);
case SNDRV_PCM_IOCTL1_FIFO_SIZE:
return snd_pcm_lib_ioctl_fifo_size(substream, arg);
+ case SNDRV_PCM_IOCTL1_SYNC_ID:
+ return snd_pcm_lib_ioctl_sync_id(substream, arg);
}
return -ENXIO;
}
@@ -2556,6 +2587,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
.info = pcm_chmap_ctl_info,
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 521ba56392a0..4057f9f10aee 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -533,6 +533,12 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
SNDRV_PCM_INFO_MMAP_VALID);
}
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_SYNC_ID,
+ params);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -1775,6 +1781,8 @@ static int snd_pcm_pre_resume(struct snd_pcm_substream *substream,
snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->state != SNDRV_PCM_STATE_SUSPENDED)
+ return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
return -ENOSYS;
runtime->trigger_master = substream;
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 42a705141050..8c4ee5066afe 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1718,6 +1718,8 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
tempo->ppq = tmr->ppq;
tempo->skew_value = tmr->skew;
tempo->skew_base = tmr->skew_base;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = tmr->tempo_base;
queuefree(queue);
return 0;
@@ -1739,6 +1741,8 @@ static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
struct snd_seq_queue_tempo *tempo = arg;
int result;
+ if (client->user_pversion < SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = 0;
result = snd_seq_set_queue_tempo(client->number, tempo);
return result < 0 ? result : 0;
}
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 500ee6b19c71..5df26788dda4 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -460,7 +460,8 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
return -EPERM;
}
- result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq);
+ result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq,
+ info->tempo_base);
if (result >= 0 && info->skew_base > 0)
result = snd_seq_timer_set_skew(q->timer, info->skew_value,
info->skew_base);
@@ -724,7 +725,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
tmr = q->timer;
if (tmr->tempo)
- bpm = 60000000 / tmr->tempo;
+ bpm = (60000 * tmr->tempo_base) / tmr->tempo;
else
bpm = 0;
@@ -741,6 +742,7 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
+ snd_iprintf(buffer, "tempo base : %d ns\n", tmr->tempo_base);
snd_iprintf(buffer, "current BPM : %d\n", bpm);
snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index ad2b97e2762d..c9f0392ac7f1 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -20,14 +20,17 @@
static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
{
- if (tmr->tempo < 1000000)
- tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
+ unsigned int threshold =
+ tmr->tempo_base == 1000 ? 1000000 : 10000;
+
+ if (tmr->tempo < threshold)
+ tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
else {
/* might overflow.. */
unsigned int s;
s = tmr->tempo % tmr->ppq;
- s = (s * 1000) / tmr->ppq;
- tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+ s = (s * tmr->tempo_base) / tmr->ppq;
+ tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
tmr->tick.resolution += s;
}
if (tmr->tick.resolution <= 0)
@@ -79,6 +82,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
+ tmr->tempo_base = 1000; /* 1us */
snd_seq_timer_set_tick_resolution(tmr);
tmr->running = 0;
@@ -164,8 +168,9 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
return 0;
}
-/* set current tempo and ppq in a shot */
-int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
+/* set current tempo, ppq and base in a shot */
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base)
{
int changed;
@@ -173,6 +178,9 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
return -EINVAL;
if (tempo <= 0 || ppq <= 0)
return -EINVAL;
+ /* allow only 10ns or 1us tempo base for now */
+ if (tempo_base && tempo_base != 10 && tempo_base != 1000)
+ return -EINVAL;
guard(spinlock_irqsave)(&tmr->lock);
if (tmr->running && (ppq != tmr->ppq)) {
/* refuse to change ppq on running timers */
@@ -183,6 +191,7 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
tmr->tempo = tempo;
tmr->ppq = ppq;
+ tmr->tempo_base = tempo_base ? tempo_base : 1000;
if (changed)
snd_seq_timer_set_tick_resolution(tmr);
return 0;
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 4bec57df8158..3b906064bde1 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -36,6 +36,7 @@ struct snd_seq_timer {
unsigned int skew;
unsigned int skew_base;
+ unsigned int tempo_base;
struct timespec64 last_update; /* time of last clock update, used for interpolation */
@@ -116,7 +117,8 @@ int snd_seq_timer_stop(struct snd_seq_timer *tmr);
int snd_seq_timer_start(struct snd_seq_timer *tmr);
int snd_seq_timer_continue(struct snd_seq_timer *tmr);
int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);
-int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base);
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c
index 171fb75267af..e90b27a135e6 100644
--- a/sound/core/seq/seq_ump_convert.c
+++ b/sound/core/seq/seq_ump_convert.c
@@ -791,7 +791,8 @@ static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
- union snd_ump_midi2_msg *data)
+ union snd_ump_midi2_msg *data,
+ unsigned char channel)
{
if (cc->rpn_set) {
data->rpn.status = UMP_MSG_STATUS_RPN;
@@ -808,6 +809,7 @@ static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
}
data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
cc->cc_data_lsb);
+ data->rpn.channel = channel;
cc->cc_data_msb = cc->cc_data_lsb = 0;
}
@@ -855,7 +857,7 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
cc->cc_data_lsb = val;
if (!(cc->rpn_set || cc->nrpn_set))
return 0; // skip
- fill_rpn(cc, data);
+ fill_rpn(cc, data, channel);
return 1;
}
@@ -957,7 +959,7 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
cc->cc_data_lsb = lsb;
if (!(cc->rpn_set || cc->nrpn_set))
return 0; // skip
- fill_rpn(cc, data);
+ fill_rpn(cc, data, channel);
return 1;
}
@@ -1018,7 +1020,7 @@ static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event,
union snd_ump_midi2_msg *data,
unsigned char status)
{
- return system_1p_ev_to_ump_midi1(event, dest_port,
+ return system_2p_ev_to_ump_midi1(event, dest_port,
(union snd_ump_midi1_msg *)data,
status);
}
@@ -1075,6 +1077,8 @@ static const struct seq_ev_to_ump seq_ev_ump_encoders[] = {
system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
{ SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING,
system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_RESET, UMP_SYSTEM_STATUS_RESET,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
};
static const struct seq_ev_to_ump *find_ump_encoder(int type)
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 04a57f7be6ea..c657659b236c 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -199,6 +199,12 @@ static int follower_put(struct snd_kcontrol *kcontrol,
if (err < 0)
return err;
for (ch = 0; ch < follower->info.count; ch++) {
+ if (ucontrol->value.integer.value[ch] < follower->info.min_val ||
+ ucontrol->value.integer.value[ch] > follower->info.max_val)
+ return -EINVAL;
+ }
+
+ for (ch = 0; ch < follower->info.count; ch++) {
if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
changed = 1;
follower->vals[ch] = ucontrol->value.integer.value[ch];
@@ -365,6 +371,8 @@ static int master_put(struct snd_kcontrol *kcontrol,
new_val = ucontrol->value.integer.value[0];
if (new_val == old_val)
return 0;
+ if (new_val < master->info.min_val || new_val > master->info.max_val)
+ return -EINVAL;
err = sync_followers(master, old_val, new_val);
if (err < 0)
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index 068c16e52dff..3fbb9793dcfc 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -665,6 +665,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = {
{ 0x19e5, "Huawei" },
{ 0x1aec, "Wolfson Microelectronics" },
{ 0x1af4, "QEMU" },
+ { 0x1fa8, "Senarytech" },
{ 0x434d, "C-Media" },
{ 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
index 5d8e1d944b0a..7b276047f85a 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/sound/hda/hdmi_chmap.c
@@ -753,6 +753,20 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
+/* a simple sanity check for input values to chmap kcontrol */
+static int chmap_value_check(struct hdac_chmap *hchmap,
+ const struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+
+ for (i = 0; i < hchmap->channels_max; i++) {
+ if (ucontrol->value.integer.value[i] < 0 ||
+ ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -764,6 +778,10 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
unsigned char chmap[8], per_pin_chmap[8];
int i, err, ca, prepared = 0;
+ err = chmap_value_check(hchmap, ucontrol);
+ if (err < 0)
+ return err;
+
/* No monitor is connected in dyn_pcm_assign.
* It's invalid to setup the chmap
*/
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index 537863447358..913880b09065 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -18,7 +18,7 @@
static int dsp_driver;
module_param(dsp_driver, int, 0444);
-MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
+MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF, 4=AVS)");
#define FLAG_SST BIT(0)
#define FLAG_SOF BIT(1)
@@ -543,6 +543,15 @@ static const struct config_entry config_table[] = {
.device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
},
#endif
+
+ /* Panther Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_PANTHERLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_PTL,
+ },
+#endif
+
};
static const struct config_entry *snd_intel_dsp_find_config
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index a6405772d537..af478c36ce5b 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1039,10 +1039,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
return 0;
__error:
- for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- if (emu->controls[i])
- snd_ctl_remove(card, emu->controls[i]);
- }
+ for (i = 0; i < EMU8000_NUM_CONTROLS; i++)
+ snd_ctl_remove(card, emu->controls[i]);
return err;
}
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index 8d8357019719..fdb992733bde 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -1080,14 +1080,10 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
card = p->chip->card;
- if (p->qsound_switch) {
- snd_ctl_remove(card, p->qsound_switch);
- p->qsound_switch = NULL;
- }
- if (p->qsound_space) {
- snd_ctl_remove(card, p->qsound_space);
- p->qsound_space = NULL;
- }
+ snd_ctl_remove(card, p->qsound_switch);
+ p->qsound_switch = NULL;
+ snd_ctl_remove(card, p->qsound_space);
+ p->qsound_space = NULL;
/* cancel pending transfer of QSound parameters */
spin_lock_irqsave (&p->q_lock, flags);
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 164335d3c200..4b1baf4dd50e 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -204,6 +204,7 @@ module_param(numWriteBufs, int, 0);
static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */
module_param(writeBufSize, int, 0);
+MODULE_DESCRIPTION("Atari/Amiga/Q40 core DMA sound driver");
MODULE_LICENSE("GPL");
static int sq_unit = -1;
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index e7f097cae574..a9a75891f1da 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -174,11 +174,6 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea
if (err < 0)
return err;
- runtime->sync.id32[0] = substream->pcm->card->number;
- runtime->sync.id32[1] = 'P';
- runtime->sync.id32[2] = 16;
- runtime->sync.id32[3] = 'V';
-
return 0;
}
@@ -226,6 +221,17 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
return snd_p16v_pcm_open_capture_channel(substream, 0);
}
+static int snd_p16v_pcm_ioctl_playback(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ if (cmd == SNDRV_PCM_IOCTL1_SYNC_ID) {
+ static const unsigned char id[4] = { 'P', '1', '6', 'V' };
+ snd_pcm_set_sync_per_card(substream, arg, id, 4);
+ return 0;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
/* prepare playback callback */
static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
@@ -531,6 +537,7 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
.open = snd_p16v_pcm_open_playback_front,
.close = snd_p16v_pcm_close_playback,
+ .ioctl = snd_p16v_pcm_ioctl_playback,
.prepare = snd_p16v_pcm_prepare_playback,
.trigger = snd_p16v_pcm_trigger_playback,
.pointer = snd_p16v_pcm_pointer_playback,
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 0da625533afc..bb15a0248250 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -162,6 +162,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
depends on ACPI || COMPILE_TEST
depends on SND_SOC
select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
select SND_HDA_GENERIC
select SND_SOC_CS35L56_SHARED
select SND_HDA_SCODEC_CS35L56
@@ -178,6 +179,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
depends on ACPI || COMPILE_TEST
depends on SND_SOC
select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
select SND_HDA_GENERIC
select SND_SOC_CS35L56_SHARED
select SND_HDA_SCODEC_CS35L56
@@ -292,6 +294,17 @@ config SND_HDA_CODEC_CONEXANT
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m
+config SND_HDA_CODEC_SENARYTECH
+ tristate "Build Senarytech HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include Senarytech HD-audio codec support in
+ snd-hda-intel driver, such as SN6186.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m
+
config SND_HDA_CODEC_CA0110
tristate "Build Creative CA0110-IBG codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 058ca0a289e4..80210f845df2 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -24,6 +24,7 @@ snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o
snd-hda-codec-ca0110-y := patch_ca0110.o
snd-hda-codec-ca0132-y := patch_ca0132.o
snd-hda-codec-conexant-y := patch_conexant.o
+snd-hda-codec-senarytech-y :=patch_senarytech.o
snd-hda-codec-via-y := patch_via.o
snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o
obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o
obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
+obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o
obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 6c49e5c6cd20..4b411ed8c3fe 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -1419,27 +1419,29 @@ static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, struct dev
static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
unsigned int sleep_flags;
int ret = 0;
- if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS)
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
return -EINVAL;
- comps = &comps[cs35l41->index];
- if (comps->dev)
+ if (comp->dev)
return -EBUSY;
pm_runtime_get_sync(dev);
mutex_lock(&cs35l41->fw_mutex);
- comps->dev = dev;
+ comp->dev = dev;
+ cs35l41->codec = parent->codec;
if (!cs35l41->acpi_subsystem_id)
cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
- comps->codec->core.subsystem_id);
- cs35l41->codec = comps->codec;
- strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+ cs35l41->codec->core.subsystem_id);
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
cs35l41->firmware_type = HDA_CS_DSP_FW_SPK_PROT;
@@ -1454,13 +1456,13 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
ret = cs35l41_create_controls(cs35l41);
- comps->playback_hook = cs35l41_hda_playback_hook;
- comps->pre_playback_hook = cs35l41_hda_pre_playback_hook;
- comps->post_playback_hook = cs35l41_hda_post_playback_hook;
- comps->acpi_notify = cs35l41_acpi_device_notify;
- comps->adev = cs35l41->dacpi;
+ comp->playback_hook = cs35l41_hda_playback_hook;
+ comp->pre_playback_hook = cs35l41_hda_pre_playback_hook;
+ comp->post_playback_hook = cs35l41_hda_post_playback_hook;
+ comp->acpi_notify = cs35l41_acpi_device_notify;
+ comp->adev = cs35l41->dacpi;
- comps->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comps->adev),
+ comp->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comp->adev),
CS35L41_DSM_GET_MUTE);
cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41,
@@ -1469,7 +1471,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
mutex_unlock(&cs35l41->fw_mutex);
sleep_flags = lock_system_sleep();
- if (!device_link_add(&comps->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
+ if (!device_link_add(&cs35l41->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
dev_warn(dev, "Unable to create device link\n");
unlock_system_sleep(sleep_flags);
@@ -1489,14 +1491,19 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
unsigned int sleep_flags;
- if (comps[cs35l41->index].dev == dev) {
- memset(&comps[cs35l41->index], 0, sizeof(*comps));
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
+ return;
+
+ if (comp->dev == dev) {
sleep_flags = lock_system_sleep();
- device_link_remove(&comps->codec->core.dev, cs35l41->dev);
+ device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev);
unlock_system_sleep(sleep_flags);
+ memset(comp, 0, sizeof(*comp));
}
}
@@ -1746,38 +1753,14 @@ int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int
return speaker_id;
}
-static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id)
{
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
u32 values[HDA_MAX_COMPONENTS];
- struct acpi_device *adev;
- struct device *physdev;
- struct spi_device *spi;
- const char *sub;
char *property;
size_t nval;
int i, ret;
- adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
- if (!adev) {
- dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
- return -ENODEV;
- }
-
- cs35l41->dacpi = adev;
- physdev = get_device(acpi_get_first_physical_node(adev));
-
- sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
- if (IS_ERR(sub))
- sub = NULL;
- cs35l41->acpi_subsystem_id = sub;
-
- ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
- if (!ret) {
- dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
- goto out;
- }
-
property = "cirrus,dev-index";
ret = device_property_count_u32(physdev, property);
if (ret <= 0)
@@ -1809,8 +1792,9 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
/* To use the same release code for all laptop variants we can't use devm_ version of
* gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node
*/
- cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(adev), "reset", cs35l41->index,
- GPIOD_OUT_LOW, "cs35l41-reset");
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset",
+ cs35l41->index, GPIOD_OUT_LOW,
+ "cs35l41-reset");
property = "cirrus,speaker-position";
ret = device_property_read_u32_array(physdev, property, values, nval);
@@ -1866,6 +1850,51 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
hw_cfg->bst_type = CS35L41_EXT_BOOST;
hw_cfg->valid = true;
+
+ return 0;
+err:
+ dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
+ hw_cfg->valid = false;
+ hw_cfg->gpio1.valid = false;
+ hw_cfg->gpio2.valid = false;
+ acpi_dev_put(cs35l41->dacpi);
+
+ return ret;
+}
+
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
+{
+ struct acpi_device *adev;
+ struct device *physdev;
+ struct spi_device *spi;
+ const char *sub;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ cs35l41->dacpi = adev;
+ physdev = get_device(acpi_get_first_physical_node(adev));
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub))
+ sub = NULL;
+ cs35l41->acpi_subsystem_id = sub;
+
+ ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
+ if (!ret) {
+ dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
+ goto out;
+ }
+
+ ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+ if (ret) {
+ put_device(physdev);
+ return ret;
+ }
out:
put_device(physdev);
@@ -1881,16 +1910,6 @@ out:
}
return 0;
-
-err:
- dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
- hw_cfg->valid = false;
- hw_cfg->gpio1.valid = false;
- hw_cfg->gpio2.valid = false;
- acpi_dev_put(cs35l41->dacpi);
- put_device(physdev);
-
- return ret;
}
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
@@ -2019,6 +2038,8 @@ void cs35l41_hda_remove(struct device *dev)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+
pm_runtime_get_sync(cs35l41->dev);
pm_runtime_dont_use_autosuspend(cs35l41->dev);
pm_runtime_disable(cs35l41->dev);
@@ -2026,8 +2047,6 @@ void cs35l41_hda_remove(struct device *dev)
if (cs35l41->halo_initialized)
cs35l41_remove_dsp(cs35l41);
- component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
-
acpi_dev_put(cs35l41->dacpi);
pm_runtime_put_noidle(cs35l41->dev);
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index b0bebb778462..c730b3351589 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -104,5 +104,6 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
struct regmap *regmap, enum control_bus control_bus);
void cs35l41_hda_remove(struct device *dev);
int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id);
#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c
index 6a7a6d486916..61d2314834e7 100644
--- a/sound/pci/hda/cs35l41_hda_property.c
+++ b/sound/pci/hda/cs35l41_hda_property.c
@@ -128,6 +128,10 @@ static const struct cs35l41_config cs35l41_config_table[] = {
{ "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{}
};
@@ -424,6 +428,20 @@ static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *phy
return 0;
}
+static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ int ret;
+
+ ret = cs35l41_add_gpios(cs35l41, physdev, -1, 2, -1, 2);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret);
+ return ret;
+ }
+
+ return cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+}
+
struct cs35l41_prop_model {
const char *hid;
const char *ssid;
@@ -497,6 +515,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "104317F3", generic_dsd_config },
{ "CSC3551", "10431863", generic_dsd_config },
{ "CSC3551", "104318D3", generic_dsd_config },
+ { "CSC3551", "10431A63", missing_speaker_id_gpio2 },
{ "CSC3551", "10431A83", generic_dsd_config },
{ "CSC3551", "10431B93", generic_dsd_config },
{ "CSC3551", "10431C9F", generic_dsd_config },
@@ -529,6 +548,10 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "17AA38B5", generic_dsd_config },
{ "CSC3551", "17AA38B6", generic_dsd_config },
{ "CSC3551", "17AA38B7", generic_dsd_config },
+ { "CSC3551", "17AA38C7", generic_dsd_config },
+ { "CSC3551", "17AA38C8", generic_dsd_config },
+ { "CSC3551", "17AA38F9", generic_dsd_config },
+ { "CSC3551", "17AA38FA", generic_dsd_config },
{}
};
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c
index 11b0570ff56d..96d3f13c5abf 100644
--- a/sound/pci/hda/cs35l56_hda.c
+++ b/sound/pci/hda/cs35l56_hda.c
@@ -50,11 +50,19 @@ static const struct reg_sequence cs35l56_hda_dai_config[] = {
};
+static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
{
unsigned int val;
int ret;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
pm_runtime_get_sync(cs35l56->base.dev);
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
if (ret == 0) {
@@ -180,6 +188,8 @@ static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
unsigned int reg_val;
int i;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
reg_val &= CS35L56_ASP_TXn_SRC_MASK;
@@ -203,6 +213,8 @@ static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
if (item >= CS35L56_NUM_INPUT_SRC)
return -EINVAL;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
&changed);
@@ -227,6 +239,8 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
unsigned int pos;
int ret;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
if (ret)
return ret;
@@ -248,6 +262,8 @@ static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
(pos > CS35L56_MAIN_POSTURE_MAX))
return -EINVAL;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
ret = regmap_update_bits_check(cs35l56->base.regmap,
CS35L56_MAIN_POSTURE_NUMBER,
CS35L56_MAIN_POSTURE_MASK,
@@ -291,6 +307,8 @@ static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
int vol;
int ret;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
if (ret)
@@ -323,6 +341,8 @@ static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
ret = regmap_update_bits_check(cs35l56->base.regmap,
CS35L56_MAIN_RENDER_USER_VOLUME,
CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
@@ -539,8 +559,9 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw
kfree(coeff_filename);
}
-static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
+static void cs35l56_hda_create_dsp_controls_work(struct work_struct *work)
{
+ struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, control_work);
struct hda_cs_dsp_ctl_info info;
info.device_name = cs35l56->amp_name;
@@ -566,7 +587,7 @@ static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
dev_info(cs35l56->base.dev, "Calibration applied\n");
}
-static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
+static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
{
const struct firmware *coeff_firmware = NULL;
const struct firmware *wmfw_firmware = NULL;
@@ -574,15 +595,34 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
char *wmfw_filename = NULL;
unsigned int preloaded_fw_ver;
bool firmware_missing;
- int ret = 0;
+ bool add_dsp_controls_required = false;
+ int ret;
+
+ /*
+ * control_work must be flushed before proceeding, but we can't do that
+ * here as it would create a deadlock on controls_rwsem so it must be
+ * performed before queuing dsp_work.
+ */
+ WARN_ON_ONCE(work_busy(&cs35l56->control_work));
- /* Prepare for a new DSP power-up */
+ /*
+ * Prepare for a new DSP power-up. If the DSP has had firmware
+ * downloaded previously then it needs to be powered down so that it
+ * can be updated and if hadn't been patched before then the controls
+ * will need to be added once firmware download succeeds.
+ */
if (cs35l56->base.fw_patched)
cs_dsp_power_down(&cs35l56->cs_dsp);
+ else
+ add_dsp_controls_required = true;
cs35l56->base.fw_patched = false;
- pm_runtime_get_sync(cs35l56->base.dev);
+ ret = pm_runtime_resume_and_get(cs35l56->base.dev);
+ if (ret < 0) {
+ dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret);
+ return;
+ }
/*
* The firmware can only be upgraded if it is currently running
@@ -606,7 +646,6 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
*/
if (!coeff_firmware && firmware_missing) {
dev_err(cs35l56->base.dev, ".bin file required but not found\n");
- ret = -ENOENT;
goto err_fw_release;
}
@@ -659,6 +698,15 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
CS35L56_FIRMWARE_MISSING);
cs35l56->base.fw_patched = true;
+ /*
+ * Adding controls is deferred to prevent a lock inversion - ALSA takes
+ * the controls_rwsem when adding a control, the get() / put()
+ * functions of a control are called holding controls_rwsem and those
+ * that depend on running firmware wait for dsp_work() to complete.
+ */
+ if (add_dsp_controls_required)
+ queue_work(system_long_wq, &cs35l56->control_work);
+
ret = cs_dsp_run(&cs35l56->cs_dsp);
if (ret)
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
@@ -678,34 +726,37 @@ err_fw_release:
coeff_firmware, coeff_filename);
err_pm_put:
pm_runtime_put(cs35l56->base.dev);
+}
- return ret;
+static void cs35l56_hda_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work);
+
+ cs35l56_hda_fw_load(cs35l56);
}
static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
- int ret;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
- if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS)
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (!comp)
return -EINVAL;
- comps = &comps[cs35l56->index];
- if (comps->dev)
+ if (comp->dev)
return -EBUSY;
- comps->dev = dev;
- cs35l56->codec = comps->codec;
- strscpy(comps->name, dev_name(dev), sizeof(comps->name));
- comps->playback_hook = cs35l56_hda_playback_hook;
+ comp->dev = dev;
+ cs35l56->codec = parent->codec;
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+ comp->playback_hook = cs35l56_hda_playback_hook;
- ret = cs35l56_hda_fw_load(cs35l56);
- if (ret)
- return ret;
+ flush_work(&cs35l56->control_work);
+ queue_work(system_long_wq, &cs35l56->dsp_work);
cs35l56_hda_create_controls(cs35l56);
- cs35l56_hda_add_dsp_controls(cs35l56);
#if IS_ENABLED(CONFIG_SND_DEBUG)
cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
@@ -720,7 +771,11 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas
static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ cancel_work_sync(&cs35l56->dsp_work);
+ cancel_work_sync(&cs35l56->control_work);
cs35l56_hda_remove_controls(cs35l56);
@@ -732,8 +787,11 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
if (cs35l56->base.fw_patched)
cs_dsp_power_down(&cs35l56->cs_dsp);
- if (comps[cs35l56->index].dev == dev)
- memset(&comps[cs35l56->index], 0, sizeof(*comps));
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (comp && (comp->dev == dev))
+ memset(comp, 0, sizeof(*comp));
+
+ cs35l56->codec = NULL;
dev_dbg(cs35l56->base.dev, "Unbound\n");
}
@@ -747,6 +805,9 @@ static int cs35l56_hda_system_suspend(struct device *dev)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+ flush_work(&cs35l56->control_work);
+
if (cs35l56->playing)
cs35l56_hda_pause(cs35l56);
@@ -840,13 +901,13 @@ static int cs35l56_hda_system_resume(struct device *dev)
cs35l56->suspended = false;
+ if (!cs35l56->codec)
+ return 0;
+
ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
- if (ret > 0) {
- ret = cs35l56_hda_fw_load(cs35l56);
- if (ret)
- return ret;
- }
+ if (ret > 0)
+ queue_work(system_long_wq, &cs35l56->dsp_work);
if (cs35l56->playing)
cs35l56_hda_play(cs35l56);
@@ -964,6 +1025,9 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
mutex_init(&cs35l56->base.irq_lock);
dev_set_drvdata(cs35l56->base.dev, cs35l56);
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work);
+ INIT_WORK(&cs35l56->control_work, cs35l56_hda_create_dsp_controls_work);
+
ret = cs35l56_hda_read_acpi(cs35l56, hid, id);
if (ret)
goto err;
@@ -1072,12 +1136,12 @@ void cs35l56_hda_remove(struct device *dev)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+
pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
pm_runtime_get_sync(cs35l56->base.dev);
pm_runtime_disable(cs35l56->base.dev);
- component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
-
cs_dsp_remove(&cs35l56->cs_dsp);
kfree(cs35l56->system_name);
diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h
index 464e4aa63cd1..c40d159507c2 100644
--- a/sound/pci/hda/cs35l56_hda.h
+++ b/sound/pci/hda/cs35l56_hda.h
@@ -14,6 +14,7 @@
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <sound/cs35l56.h>
struct dentry;
@@ -21,6 +22,8 @@ struct dentry;
struct cs35l56_hda {
struct cs35l56_base base;
struct hda_codec *codec;
+ struct work_struct dsp_work;
+ struct work_struct control_work;
int index;
const char *system_name;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 325e8f0b99a8..3dd1bda0c5c6 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1496,7 +1496,7 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
/* ofs = 0: raw max value */
maxval = get_amp_max_value(codec, nid, dir, 0);
if (val > maxval)
- val = maxval;
+ return -EINVAL;
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
HDA_AMP_VOLMASK, val);
}
@@ -1547,13 +1547,21 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value;
int change = 0;
+ int err;
if (chs & 1) {
- change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+ err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
valp++;
}
- if (chs & 2)
- change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+ if (chs & 2) {
+ err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
+ }
return change;
}
EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
@@ -2149,15 +2157,20 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
int change = 0;
if (chs & 1) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
valp++;
}
- if (chs & 2)
+ if (chs & 2) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
+ }
hda_call_check_power_status(codec, nid);
return change;
}
diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c
index d02589014a3f..7b19cb38b4e0 100644
--- a/sound/pci/hda/hda_component.c
+++ b/sound/pci/hda/hda_component.c
@@ -15,35 +15,41 @@
#include "hda_local.h"
#ifdef CONFIG_ACPI
-void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps,
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
acpi_handle handle, u32 event, void *data)
{
+ struct hda_component *comp;
int i;
- for (i = 0; i < num_comps; i++) {
- if (comps[i].dev && comps[i].acpi_notify)
- comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event,
- comps[i].dev);
+ mutex_lock(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->acpi_notify)
+ comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev);
}
+ mutex_unlock(&parent->mutex);
}
EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT);
int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps, int num_comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler, void *data)
{
bool support_notifications = false;
struct acpi_device *adev;
+ struct hda_component *comp;
int ret;
int i;
- adev = comps[0].adev;
+ adev = parent->comps[0].adev;
if (!acpi_device_handle(adev))
return 0;
- for (i = 0; i < num_comps; i++)
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
support_notifications = support_notifications ||
- comps[i].acpi_notifications_supported;
+ comp->acpi_notifications_supported;
+ }
if (support_notifications) {
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
@@ -61,13 +67,13 @@ int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler)
{
struct acpi_device *adev;
int ret;
- adev = comps[0].adev;
+ adev = parent->comps[0].adev;
if (!acpi_device_handle(adev))
return;
@@ -78,22 +84,28 @@ void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
#endif /* ifdef CONFIG_ACPI */
-void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action)
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)
{
+ struct hda_component *comp;
int i;
- for (i = 0; i < num_comps; i++) {
- if (comps[i].dev && comps[i].pre_playback_hook)
- comps[i].pre_playback_hook(comps[i].dev, action);
+ mutex_lock(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->pre_playback_hook)
+ comp->pre_playback_hook(comp->dev, action);
}
- for (i = 0; i < num_comps; i++) {
- if (comps[i].dev && comps[i].playback_hook)
- comps[i].playback_hook(comps[i].dev, action);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->playback_hook)
+ comp->playback_hook(comp->dev, action);
}
- for (i = 0; i < num_comps; i++) {
- if (comps[i].dev && comps[i].post_playback_hook)
- comps[i].post_playback_hook(comps[i].dev, action);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->post_playback_hook)
+ comp->post_playback_hook(comp->dev, action);
}
+ mutex_unlock(&parent->mutex);
}
EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT);
@@ -124,22 +136,25 @@ static int hda_comp_match_dev_name(struct device *dev, void *data)
}
int hda_component_manager_bind(struct hda_codec *cdc,
- struct hda_component *comps, int count)
+ struct hda_component_parent *parent)
{
- int i;
+ int ret;
- /* Init shared data */
- for (i = 0; i < count; ++i) {
- memset(&comps[i], 0, sizeof(comps[i]));
- comps[i].codec = cdc;
- }
+ /* Init shared and component specific data */
+ memset(parent, 0, sizeof(*parent));
+ mutex_init(&parent->mutex);
+ parent->codec = cdc;
+
+ mutex_lock(&parent->mutex);
+ ret = component_bind_all(hda_codec_dev(cdc), parent);
+ mutex_unlock(&parent->mutex);
- return component_bind_all(hda_codec_dev(cdc), comps);
+ return ret;
}
EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, SND_HDA_SCODEC_COMPONENT);
int hda_component_manager_init(struct hda_codec *cdc,
- struct hda_component *comps, int count,
+ struct hda_component_parent *parent, int count,
const char *bus, const char *hid,
const char *match_str,
const struct component_master_ops *ops)
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
index c70b3de68ab2..9f786608144c 100644
--- a/sound/pci/hda/hda_component.h
+++ b/sound/pci/hda/hda_component.h
@@ -11,6 +11,7 @@
#include <linux/acpi.h>
#include <linux/component.h>
+#include <linux/mutex.h>
#include <sound/hda_codec.h>
#define HDA_MAX_COMPONENTS 4
@@ -19,7 +20,6 @@
struct hda_component {
struct device *dev;
char name[HDA_MAX_NAME_SIZE];
- struct hda_codec *codec;
struct acpi_device *adev;
bool acpi_notifications_supported;
void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev);
@@ -28,18 +28,23 @@ struct hda_component {
void (*post_playback_hook)(struct device *dev, int action);
};
+struct hda_component_parent {
+ struct mutex mutex;
+ struct hda_codec *codec;
+ struct hda_component comps[HDA_MAX_COMPONENTS];
+};
+
#ifdef CONFIG_ACPI
-void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps,
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
acpi_handle handle, u32 event, void *data);
int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps, int num_comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler, void *data);
void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler);
#else
-static inline void hda_component_acpi_device_notify(struct hda_component *comps,
- int num_comps,
+static inline void hda_component_acpi_device_notify(struct hda_component_parent *parent,
acpi_handle handle,
u32 event,
void *data)
@@ -47,8 +52,7 @@ static inline void hda_component_acpi_device_notify(struct hda_component *comps,
}
static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps,
- int num_comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler,
void *data)
@@ -57,17 +61,16 @@ static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec
}
static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
- struct hda_component *comps,
+ struct hda_component_parent *parent,
acpi_notify_handler handler)
{
}
#endif /* ifdef CONFIG_ACPI */
-void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps,
- int action);
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action);
int hda_component_manager_init(struct hda_codec *cdc,
- struct hda_component *comps, int count,
+ struct hda_component_parent *parent, int count,
const char *bus, const char *hid,
const char *match_str,
const struct component_master_ops *ops);
@@ -75,13 +78,26 @@ int hda_component_manager_init(struct hda_codec *cdc,
void hda_component_manager_free(struct hda_codec *cdc,
const struct component_master_ops *ops);
-int hda_component_manager_bind(struct hda_codec *cdc,
- struct hda_component *comps, int count);
+int hda_component_manager_bind(struct hda_codec *cdc, struct hda_component_parent *parent);
+
+static inline struct hda_component *hda_component_from_index(struct hda_component_parent *parent,
+ int index)
+{
+ if (!parent)
+ return NULL;
+
+ if (index < 0 || index >= ARRAY_SIZE(parent->comps))
+ return NULL;
+
+ return &parent->comps[index];
+}
static inline void hda_component_manager_unbind(struct hda_codec *cdc,
- struct hda_component *comps)
+ struct hda_component_parent *parent)
{
- component_unbind_all(hda_codec_dev(cdc), comps);
+ mutex_lock(&parent->mutex);
+ component_unbind_all(hda_codec_dev(cdc), parent);
+ mutex_unlock(&parent->mutex);
}
#endif /* ifndef __HDA_COMPONENT_H__ */
diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c
index e6e876998e71..deb74c247082 100644
--- a/sound/pci/hda/hda_cs_dsp_ctl.c
+++ b/sound/pci/hda/hda_cs_dsp_ctl.c
@@ -207,7 +207,7 @@ void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
/* ctl and kctl may already have been removed by ALSA private_free */
- if (ctl && ctl->kctl)
+ if (ctl)
snd_ctl_remove(ctl->card, ctl->kctl);
}
EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3500108f6ba3..b33602e64d17 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2495,6 +2495,8 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Arrow Lake */
{ PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Panther Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Apollolake (Broxton-P) */
{ PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Gemini-Lake */
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index aa76d1c88589..75f9faaad313 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -131,7 +131,7 @@ struct alc_spec {
u8 alc_mute_keycode_map[1];
/* component binding */
- struct hda_component comps[HDA_MAX_COMPONENTS];
+ struct hda_component_parent comps;
};
/*
@@ -583,10 +583,14 @@ static void alc_shutup_pins(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0236:
case 0x10ec0256:
+ case 0x10ec0257:
case 0x19e58326:
case 0x10ec0283:
+ case 0x10ec0285:
case 0x10ec0286:
+ case 0x10ec0287:
case 0x10ec0288:
+ case 0x10ec0295:
case 0x10ec0298:
alc_headset_mic_no_shutup(codec);
break;
@@ -6789,8 +6793,7 @@ static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
codec_info(cdc, "ACPI Notification %d\n", event);
- hda_component_acpi_device_notify(spec->comps, ARRAY_SIZE(spec->comps),
- handle, event, data);
+ hda_component_acpi_device_notify(&spec->comps, handle, event, data);
}
static int comp_bind(struct device *dev)
@@ -6799,12 +6802,12 @@ static int comp_bind(struct device *dev)
struct alc_spec *spec = cdc->spec;
int ret;
- ret = hda_component_manager_bind(cdc, spec->comps, ARRAY_SIZE(spec->comps));
+ ret = hda_component_manager_bind(cdc, &spec->comps);
if (ret)
return ret;
return hda_component_manager_bind_acpi_notifications(cdc,
- spec->comps, ARRAY_SIZE(spec->comps),
+ &spec->comps,
comp_acpi_device_notify, cdc);
}
@@ -6813,8 +6816,8 @@ static void comp_unbind(struct device *dev)
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
- hda_component_manager_unbind_acpi_notifications(cdc, spec->comps, comp_acpi_device_notify);
- hda_component_manager_unbind(cdc, spec->comps);
+ hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify);
+ hda_component_manager_unbind(cdc, &spec->comps);
}
static const struct component_master_ops comp_master_ops = {
@@ -6827,7 +6830,7 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_
{
struct alc_spec *spec = cdc->spec;
- hda_component_manager_playback_hook(spec->comps, ARRAY_SIZE(spec->comps), action);
+ hda_component_manager_playback_hook(&spec->comps, action);
}
static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
@@ -6838,7 +6841,7 @@ static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bu
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
- ret = hda_component_manager_init(cdc, spec->comps, count, bus, hid,
+ ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid,
match_str, &comp_master_ops);
if (ret)
return;
@@ -7520,6 +7523,8 @@ enum {
ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1,
ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318,
ALC256_FIXUP_CHROME_BOOK,
+ ALC287_FIXUP_LENOVO_14ARP8_LEGION_IAH7,
+ ALC287_FIXUP_LENOVO_SSID_17AA3820,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -7559,6 +7564,21 @@ static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec,
__snd_hda_apply_fixup(codec, id, action, 0);
}
+/* Similar to above the Lenovo Yoga Pro 7 14ARP8 PCI SSID matches the codec SSID of the
+ Legion Y9000X 2022 IAH7.*/
+static void alc287_fixup_lenovo_14arp8_legion_iah7(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.subsystem_id == 0x17aa386e)
+ id = ALC287_FIXUP_CS35L41_I2C_2; /* Legion Y9000X 2022 IAH7 */
+ else
+ id = ALC285_FIXUP_SPEAKER2_TO_DAC1; /* Yoga Pro 7 14ARP8 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
+
/* Another hilarious PCI SSID conflict with Lenovo Legion Pro 7 16ARX8H (with
* TAS2781 codec) and Legion 7i 16IAX7 (with CS35L41 codec);
* we apply a corresponding fixup depending on the codec SSID instead
@@ -7576,6 +7596,20 @@ static void alc287_fixup_lenovo_legion_7(struct hda_codec *codec,
__snd_hda_apply_fixup(codec, id, action, 0);
}
+/* Yet more conflicting PCI SSID (17aa:3820) on two Lenovo models */
+static void alc287_fixup_lenovo_ssid_17aa3820(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.subsystem_id == 0x17aa3820)
+ id = ALC269_FIXUP_ASPIRE_HEADSET_MIC; /* IdeaPad 330-17IKB 81DM */
+ else /* 0x17aa3802 */
+ id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* "Yoga Duet 7 13ITL6 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
+
static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_GPIO2] = {
.type = HDA_FIXUP_FUNC,
@@ -9658,6 +9692,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
},
+ [ALC287_FIXUP_LENOVO_14ARP8_LEGION_IAH7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_14arp8_legion_iah7,
+ },
[ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin,
@@ -9808,6 +9846,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC225_FIXUP_HEADSET_JACK
},
+ [ALC287_FIXUP_LENOVO_SSID_17AA3820] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_ssid_17aa3820,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -10010,6 +10052,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
@@ -10045,6 +10088,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -10194,6 +10238,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
@@ -10332,6 +10383,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
+ SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
@@ -10429,6 +10482,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
@@ -10502,7 +10556,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7),
SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
- SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3820, "IdeaPad 330 / Yoga Duet 7", ALC287_FIXUP_LENOVO_SSID_17AA3820),
SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
@@ -10516,7 +10570,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
- SND_PCI_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7 / Yoga Pro 7 14ARP8", ALC287_FIXUP_LENOVO_14ARP8_LEGION_IAH7),
SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7/7i", ALC287_FIXUP_LENOVO_LEGION_7),
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),
@@ -10527,6 +10581,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
@@ -10540,10 +10595,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4),
+ SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
@@ -10581,6 +10640,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
+ SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS),
SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP),
@@ -10598,6 +10658,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
+ SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
@@ -10605,7 +10666,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0xf111, 0x0005, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
#if 0
diff --git a/sound/pci/hda/patch_senarytech.c b/sound/pci/hda/patch_senarytech.c
new file mode 100644
index 000000000000..0691996fa971
--- /dev/null
+++ b/sound/pci/hda/patch_senarytech.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio interface patch for Senary HDA audio codec
+ *
+ * Initially based on sound/pci/hda/patch_conexant.c
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+struct senary_spec {
+ struct hda_gen_spec gen;
+
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ hda_nid_t mute_led_eapd;
+
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new senary_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+};
+
+static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
+
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &senary_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+
+static int senary_auto_parse_beep(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec)
+ if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) &&
+ (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD)))
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
+}
+#else
+#define senary_auto_parse_beep(codec) 0
+#endif
+
+/* parse EAPDs */
+static void senary_auto_parse_eapd(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
+ }
+}
+
+static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void senary_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct senary_spec *spec = codec->spec;
+
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
+static void senary_init_gpio_led(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
+
+ if (mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+ }
+}
+
+static int senary_auto_init(struct hda_codec *codec)
+{
+ snd_hda_gen_init(codec);
+ senary_init_gpio_led(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+
+static void senary_auto_shutdown(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+
+ /* Turn the problematic codec into D3 to avoid spurious noises
+ * from the internal speaker during (and after) reboot
+ */
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
+}
+
+static void senary_auto_free(struct hda_codec *codec)
+{
+ senary_auto_shutdown(codec);
+ snd_hda_gen_free(codec);
+}
+
+static int senary_auto_suspend(struct hda_codec *codec)
+{
+ senary_auto_shutdown(codec);
+ return 0;
+}
+
+static const struct hda_codec_ops senary_auto_patch_ops = {
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = senary_auto_init,
+ .free = senary_auto_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = senary_auto_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+};
+
+static int patch_senary_auto(struct hda_codec *codec)
+{
+ struct senary_spec *spec;
+ int err;
+
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+ codec->patch_ops = senary_auto_patch_ops;
+
+ senary_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
+
+ if (!spec->gen.vmaster_mute.hook)
+ spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
+
+ err = senary_auto_parse_beep(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ goto error;
+
+ /* Some laptops with Senary chips show stalls in S3 resume,
+ * which falls into the single-cmd mode.
+ * Better to make reset, then.
+ */
+ if (!codec->bus->core.sync_write) {
+ codec_info(codec,
+ "Enable sync_write for stable communication\n");
+ codec->bus->core.sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ senary_auto_free(codec);
+ return err;
+}
+
+/*
+ */
+
+static const struct hda_device_id snd_hda_id_senary[] = {
+ HDA_CODEC_ENTRY(0x1fa86186, "SN6186", patch_senary_auto),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Senarytech HD-audio codec");
+
+static struct hda_codec_driver senary_driver = {
+ .id = snd_hda_id_senary,
+};
+
+module_hda_codec_driver(senary_driver);
diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c
index 10420360b214..49bd7097d892 100644
--- a/sound/pci/hda/tas2781_hda_i2c.c
+++ b/sound/pci/hda/tas2781_hda_i2c.c
@@ -597,18 +597,13 @@ static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
{
struct hda_codec *codec = tas_hda->priv->codec;
- if (tas_hda->dsp_prog_ctl)
- snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
-
- if (tas_hda->dsp_conf_ctl)
- snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--)
- if (tas_hda->snd_ctls[i])
- snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]);
+ snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]);
- if (tas_hda->prof_ctl)
- snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
}
static void tasdev_fw_ready(const struct firmware *fmw, void *context)
@@ -706,20 +701,20 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
void *master_data)
{
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
struct hda_codec *codec;
unsigned int subid;
int ret;
- if (!comps || tas_hda->priv->index < 0 ||
- tas_hda->priv->index >= HDA_MAX_COMPONENTS)
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
return -EINVAL;
- comps = &comps[tas_hda->priv->index];
- if (comps->dev)
+ if (comp->dev)
return -EBUSY;
- codec = comps->codec;
+ codec = parent->codec;
subid = codec->core.subsystem_id >> 16;
switch (subid) {
@@ -733,13 +728,13 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
pm_runtime_get_sync(dev);
- comps->dev = dev;
+ comp->dev = dev;
- strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready);
if (!ret)
- comps->playback_hook = tas2781_hda_playback_hook;
+ comp->playback_hook = tas2781_hda_playback_hook;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
@@ -751,13 +746,14 @@ static void tas2781_hda_unbind(struct device *dev,
struct device *master, void *master_data)
{
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
- struct hda_component *comps = master_data;
- comps = &comps[tas_hda->priv->index];
-
- if (comps->dev == dev) {
- comps->dev = NULL;
- memset(comps->name, 0, sizeof(comps->name));
- comps->playback_hook = NULL;
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
}
tas2781_hda_remove_controls(tas_hda);
@@ -777,11 +773,11 @@ static void tas2781_hda_remove(struct device *dev)
{
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ component_del(tas_hda->dev, &tas2781_hda_comp_ops);
+
pm_runtime_get_sync(tas_hda->dev);
pm_runtime_disable(tas_hda->dev);
- component_del(tas_hda->dev, &tas2781_hda_comp_ops);
-
pm_runtime_put_noidle(tas_hda->dev);
tasdevice_remove(tas_hda->priv);
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index dfc1fc9b701d..2894d041b2f5 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -80,8 +80,8 @@ static void keywest_remove(struct i2c_client *client)
static const struct i2c_device_id keywest_i2c_id[] = {
- { "MAC,tas3004", 0 }, /* instantiated by i2c-powermac */
- { "keywest", 0 }, /* instantiated by us if needed */
+ { "MAC,tas3004" }, /* instantiated by i2c-powermac */
+ { "keywest" }, /* instantiated by us if needed */
{ }
};
MODULE_DEVICE_TABLE(i2c, keywest_i2c_id);
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index 5648d744aa79..8f6929ced2c8 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -726,12 +726,8 @@ static int snd_at73c213_mixer(struct snd_at73c213 *chip)
return 0;
cleanup:
- for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_find_numid(card, idx);
- if (kctl)
- snd_ctl_remove(card, kctl);
- }
+ for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++)
+ snd_ctl_remove(card, snd_ctl_find_numid(card, idx));
return errval;
}
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 3b45d0ee7693..1bb6a455a1b4 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -82,13 +82,13 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
fp->fmt_bits = sample_width;
if ((pcm_formats == 0) &&
- (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
+ (format == 0 || format == BIT(UAC_FORMAT_TYPE_I_UNDEFINED))) {
/* some devices don't define this correctly... */
usb_audio_info(chip, "%u:%d : format type 0 is detected, processed as PCM\n",
fp->iface, fp->altsetting);
- format = 1 << UAC_FORMAT_TYPE_I_PCM;
+ format = BIT(UAC_FORMAT_TYPE_I_PCM);
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM)) {
if (((chip->usb_id == USB_ID(0x0582, 0x0016)) ||
/* Edirol SD-90 */
(chip->usb_id == USB_ID(0x0582, 0x000c))) &&
@@ -128,7 +128,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
break;
}
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM8)) {
/* Dallas DS4201 workaround: it advertises U8 format, but really
supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
@@ -136,15 +136,12 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
else
pcm_formats |= SNDRV_PCM_FMTBIT_U8;
}
- if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_IEEE_FLOAT))
pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_ALAW))
pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_MULAW))
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
- }
if (format & ~0x3f) {
usb_audio_info(chip,
"%u:%d : unsupported format bits %#llx\n",
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..c00009b545c0 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -433,7 +433,7 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
{
int err;
- if (cval->cached & (1 << channel)) {
+ if (cval->cached & BIT(channel)) {
*value = cval->cache_val[index];
return 0;
}
@@ -445,7 +445,7 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
cval->control, channel, err);
return err;
}
- cval->cached |= 1 << channel;
+ cval->cached |= BIT(channel);
cval->cache_val[index] = *value;
return 0;
}
@@ -522,7 +522,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
int err;
unsigned int read_only = (channel == 0) ?
cval->master_readonly :
- cval->ch_readonly & (1 << (channel - 1));
+ cval->ch_readonly & BIT(channel - 1);
if (read_only) {
usb_audio_dbg(cval->head.mixer->chip,
@@ -536,7 +536,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
value);
if (err < 0)
return err;
- cval->cached |= 1 << channel;
+ cval->cached |= BIT(channel);
cval->cache_val[index] = value;
return 0;
}
@@ -1253,7 +1253,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int minchn = 0;
if (cval->cmask) {
for (i = 0; i < MAX_CHANNELS; i++)
- if (cval->cmask & (1 << i)) {
+ if (cval->cmask & BIT(i)) {
minchn = i + 1;
break;
}
@@ -1358,7 +1358,7 @@ no_res_check:
} else {
idx = 0;
for (i = 0; i < MAX_CHANNELS; i++) {
- if (cval->cmask & (1 << i)) {
+ if (cval->cmask & BIT(i)) {
init_cur_mix_raw(cval, i + 1, idx);
idx++;
}
@@ -1416,7 +1416,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val);
if (err < 0)
@@ -1448,7 +1448,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval);
if (err < 0)
@@ -1700,7 +1700,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
} else {
int i, c = 0;
for (i = 0; i < 16; i++)
- if (ctl_mask & (1 << i))
+ if (ctl_mask & BIT(i))
c++;
cval->channels = c;
cval->ch_readonly = readonly_mask;
@@ -2014,6 +2014,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
bmaControls = ftr->bmaControls;
}
+ if (channels > 32) {
+ usb_audio_info(state->chip,
+ "usbmixer: too many channels (%d) in unit %d\n",
+ channels, unitid);
+ return -EINVAL;
+ }
+
/* parse the source unit */
err = parse_audio_unit(state, hdr->bSourceID);
if (err < 0)
@@ -2053,8 +2060,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
- if (mask & (1 << i))
- ch_bits |= (1 << j);
+ if (mask & BIT(i))
+ ch_bits |= BIT(j);
}
/* audio class v1 controls are never read-only */
@@ -2065,7 +2072,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, 0);
- if (master_bits & (1 << i))
+ if (master_bits & BIT(i))
build_feature_ctl(state, _ftr, 0, control,
&iterm, unitid, 0);
}
@@ -2081,9 +2088,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
if (uac_v2v3_control_is_readable(mask, control)) {
- ch_bits |= (1 << j);
+ ch_bits |= BIT(j);
if (!uac_v2v3_control_is_writeable(mask, control))
- ch_read_only |= (1 << j);
+ ch_read_only |= BIT(j);
}
}
@@ -2174,7 +2181,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
__u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
if (check_matrix_bitmap(c, in_ch, i, num_outs)) {
- cval->cmask |= (1 << i);
+ cval->cmask |= BIT(i);
cval->channels++;
}
}
@@ -2497,7 +2504,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
if (state->mixer->protocol == UAC_VERSION_1) {
if (!(controls[valinfo->control / 8] &
- (1 << ((valinfo->control % 8) - 1))))
+ BIT((valinfo->control % 8) - 1)))
continue;
} else { /* UAC_VERSION_2/3 */
if (!uac_v2v3_control_is_readable(controls[valinfo->control / 8],
@@ -3441,7 +3448,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
case UAC2_CS_CUR:
/* invalidate cache, so the value is read from the device */
if (channel)
- info->cached &= ~(1 << channel);
+ info->cached &= ~BIT(channel);
else /* master channel */
info->cached = 0;
@@ -3677,9 +3684,9 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)
if (cval->cmask) {
idx = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
- if (cval->cached & (1 << (c + 1))) {
+ if (cval->cached & BIT(c + 1)) {
err = snd_usb_set_cur_mix_value(cval, c + 1, idx,
cval->cache_val[idx]);
if (err < 0)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 212b5e6443d8..2bc344cf54a8 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1139,7 +1139,7 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
for (out = 0; out < 8; out++) {
control = out + 1;
for (in = 0; in < 8; in++) {
- cmask = 1 << in;
+ cmask = BIT(in);
snprintf(name, sizeof(name),
"AIn%d - Out%d Capture Volume",
in + 1, out + 1);
@@ -1150,7 +1150,7 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
return err;
}
for (in = 8; in < 16; in++) {
- cmask = 1 << in;
+ cmask = BIT(in);
snprintf(name, sizeof(name),
"DIn%d - Out%d Playback Volume",
in - 7, out + 1);
@@ -1215,7 +1215,7 @@ static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
const unsigned int control = 7;
for (ch = 0; ch < 4; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
"Effect Return %d Volume", ch + 1);
err = snd_create_std_mono_ctl(mixer, id, control,
@@ -1239,7 +1239,7 @@ static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
const unsigned int control = 9;
for (ch = 0; ch < 8; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
"Effect Send AIn%d Volume", ch + 1);
err = snd_create_std_mono_ctl(mixer, id, control, cmask,
@@ -1249,7 +1249,7 @@ static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
return err;
}
for (ch = 8; ch < 16; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
"Effect Send DIn%d Volume", ch - 7);
err = snd_create_std_mono_ctl(mixer, id, control, cmask,
@@ -1352,7 +1352,7 @@ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
chan - num_outs + 1, out + 1);
}
- cmask = (out == 0) ? 0 : 1 << (out - 1);
+ cmask = (out == 0) ? 0 : BIT(out - 1);
offset = chan * num_outs;
err = snd_create_std_mono_ctl_offset(mixer, id, control,
cmask, val_type, offset, name,
@@ -1438,7 +1438,7 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
chan - num_outs + 1);
}
- cmask = (chan == 0) ? 0 : 1 << (chan - 1);
+ cmask = (chan == 0) ? 0 : BIT(chan - 1);
err = snd_create_std_mono_ctl(mixer, id, control,
cmask, val_type, name,
&snd_usb_mixer_vol_tlv);
@@ -1480,7 +1480,7 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer
chan + 1);
cmask = (chan == 0) ? 0 :
- 1 << (chan + (chan % 2) * num_outs - 1);
+ BIT(chan + (chan % 2) * num_outs - 1);
err = snd_create_std_mono_ctl_offset(mixer, id, control,
cmask, val_type, offset, name,
&snd_usb_mixer_vol_tlv);
@@ -2568,12 +2568,12 @@ static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
usb_idx = 3;
usb_val = value ? 3 : 0;
} else {
- usb_idx = 1 << index;
+ usb_idx = BIT(index);
usb_val = value ? usb_idx : 0;
}
} else {
usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
- usb_idx = 1 << index;
+ usb_idx = BIT(index);
usb_val = value ? usb_idx : 0;
}
diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c
index 31b5dc0f34d2..b229eb6f7057 100644
--- a/sound/xen/xen_snd_front_alsa.c
+++ b/sound/xen/xen_snd_front_alsa.c
@@ -69,11 +69,6 @@ struct alsa_sndif_sample_format {
snd_pcm_format_t alsa;
};
-struct alsa_sndif_hw_param {
- u8 sndif;
- snd_pcm_hw_param_t alsa;
-};
-
static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
{
.sndif = XENSND_PCM_FORMAT_U8,
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index 1c04e5f638a0..dd74f8cc7ece 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -626,28 +626,41 @@ static int write_and_verify(struct ctl_data *ctl,
}
/*
+ * We can't verify any specific value for volatile controls
+ * but we should still check that whatever we read is a valid
+ * vale for the control.
+ */
+ if (snd_ctl_elem_info_is_volatile(ctl->info)) {
+ if (!ctl_value_valid(ctl, read_val)) {
+ ksft_print_msg("Volatile control %s has invalid value\n",
+ ctl->name);
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ /*
* Check for an event if the value changed, or confirm that
* there was none if it didn't. We rely on the kernel
* generating the notification before it returns from the
* write, this is currently true, should that ever change this
* will most likely break and need updating.
*/
- if (!snd_ctl_elem_info_is_volatile(ctl->info)) {
- err = wait_for_event(ctl, 0);
- if (snd_ctl_elem_value_compare(initial_val, read_val)) {
- if (err < 1) {
- ksft_print_msg("No event generated for %s\n",
- ctl->name);
- show_values(ctl, initial_val, read_val);
- ctl->event_missing++;
- }
- } else {
- if (err != 0) {
- ksft_print_msg("Spurious event generated for %s\n",
- ctl->name);
- show_values(ctl, initial_val, read_val);
- ctl->event_spurious++;
- }
+ err = wait_for_event(ctl, 0);
+ if (snd_ctl_elem_value_compare(initial_val, read_val)) {
+ if (err < 1) {
+ ksft_print_msg("No event generated for %s\n",
+ ctl->name);
+ show_values(ctl, initial_val, read_val);
+ ctl->event_missing++;
+ }
+ } else {
+ if (err != 0) {
+ ksft_print_msg("Spurious event generated for %s\n",
+ ctl->name);
+ show_values(ctl, initial_val, read_val);
+ ctl->event_spurious++;
}
}
diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c
index de664dedb541..914efcdce1ec 100644
--- a/tools/testing/selftests/alsa/pcm-test.c
+++ b/tools/testing/selftests/alsa/pcm-test.c
@@ -383,7 +383,7 @@ __format:
goto __close;
}
if (rrate != rate) {
- snprintf(msg, sizeof(msg), "rate mismatch %ld != %d", rate, rrate);
+ snprintf(msg, sizeof(msg), "rate mismatch %ld != %u", rate, rrate);
goto __close;
}
rperiod_size = period_size;