diff options
62 files changed, 1426 insertions, 547 deletions
diff --git a/Documentation/sound/designs/compress-accel.rst b/Documentation/sound/designs/compress-accel.rst new file mode 100644 index 000000000000..c9c1744b94c2 --- /dev/null +++ b/Documentation/sound/designs/compress-accel.rst @@ -0,0 +1,134 @@ +================================== +ALSA Co-processor Acceleration API +================================== + +Jaroslav Kysela <perex@perex.cz> + + +Overview +======== + +There is a requirement to expose the audio hardware that accelerates various +tasks for user space such as sample rate converters, compressed +stream decoders, etc. + +This is description for the API extension for the compress ALSA API which +is able to handle "tasks" that are not bound to real-time operations +and allows for the serialization of operations. + +Requirements +============ + +The main requirements are: + +- serialization of multiple tasks for user space to allow multiple + operations without user space intervention + +- separate buffers (input + output) for each operation + +- expose buffers using mmap to user space + +- signal user space when the task is finished (standard poll mechanism) + +Design +====== + +A new direction SND_COMPRESS_ACCEL is introduced to identify +the passthrough API. + +The API extension shares device enumeration and parameters handling from +the main compressed API. All other realtime streaming ioctls are deactivated +and a new set of task related ioctls are introduced. The standard +read/write/mmap I/O operations are not supported in the passthrough device. + +Device ("stream") state handling is reduced to OPEN/SETUP. All other +states are not available for the passthrough mode. + +Data I/O mechanism is using standard dma-buf interface with all advantages +like mmap, standard I/O, buffer sharing etc. One buffer is used for the +input data and second (separate) buffer is used for the output data. Each task +have separate I/O buffers. + +For the buffering parameters, the fragments means a limit of allocated tasks +for given device. The fragment_size limits the input buffer size for the given +device. The output buffer size is determined by the driver (may be different +from the input buffer size). + +State Machine +============= + +The passthrough audio stream state machine is described below:: + + +----------+ + | | + | OPEN | + | | + +----------+ + | + | + | compr_set_params() + | + v + all passthrough task ops +----------+ + +------------------------------------| | + | | SETUP | + | | + | +----------+ + | | + +------------------------------------------+ + + +Passthrough operations (ioctls) +=============================== + +All operations are protected using stream->device->lock (mutex). + +CREATE +------ +Creates a set of input/output buffers. The input buffer size is +fragment_size. Allocates unique seqno. + +The hardware drivers allocate internal 'struct dma_buf' for both input and +output buffers (using 'dma_buf_export()' function). The anonymous +file descriptors for those buffers are passed to user space. + +FREE +---- +Free a set of input/output buffers. If a task is active, the stop +operation is executed before. If seqno is zero, operation is executed for all +tasks. + +START +----- +Starts (queues) a task. There are two cases of the task start - right after +the task is created. In this case, origin_seqno must be zero. +The second case is for reusing of already finished task. The origin_seqno +must identify the task to be reused. In both cases, a new seqno value +is allocated and returned to user space. + +The prerequisite is that application filled input dma buffer with +new source data and set input_size to pass the real data size to the driver. + +The order of data processing is preserved (first started job must be +finished at first). + +If the multiple tasks require a state handling (e.g. resampling operation), +the user space may set SND_COMPRESS_TFLG_NEW_STREAM flag to mark the +start of the new stream data. It is useful to keep the allocated buffers +for the new operation rather using open/close mechanism. + +STOP +---- +Stop (dequeues) a task. If seqno is zero, operation is executed for all +tasks. + +STATUS +------ +Obtain the task status (active, finished). Also, the driver will set +the real output data size (valid area in the output buffer). + +Credits +======= +- Shengjiu Wang <shengjiu.wang@gmail.com> +- Takashi Iwai <tiwai@suse.de> +- Vinod Koul <vkoul@kernel.org> diff --git a/Documentation/sound/designs/index.rst b/Documentation/sound/designs/index.rst index b79db9ad8732..6b825c5617fc 100644 --- a/Documentation/sound/designs/index.rst +++ b/Documentation/sound/designs/index.rst @@ -6,6 +6,7 @@ Designs and Implementations control-names channel-mapping-api + compress-accel compress-offload timestamping jack-controls diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index e199131bf5ab..f81e94d8f145 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -42,7 +42,7 @@ If you are interested in the deep debugging of HD-audio, read the HD-audio specification at first. The specification is found on Intel's web page, for example: -* https://www.intel.com/standards/hdaudio/ +* https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html HD-Audio Controller diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index bcf872c17dd3..b55c9eeb2b54 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -20,6 +20,30 @@ struct snd_compr_ops; /** + * struct snd_compr_task_runtime: task runtime description + * @list: list of all managed tasks + * @input: input DMA buffer + * @output: output DMA buffer + * @seqno: sequence number + * @input_size: really used data in the input buffer + * @output_size: really used data in the output buffer + * @flags: see SND_COMPRESS_TFLG_* + * @state: actual task state + * @private_value: used by the lowlevel driver (opaque) + */ +struct snd_compr_task_runtime { + struct list_head list; + struct dma_buf *input; + struct dma_buf *output; + u64 seqno; + u64 input_size; + u64 output_size; + u32 flags; + u8 state; + void *private_value; +}; + +/** * struct snd_compr_runtime: runtime stream description * @state: stream state * @ops: pointer to DSP callbacks @@ -37,6 +61,10 @@ struct snd_compr_ops; * @dma_addr: physical buffer address (not accessible from main CPU) * @dma_bytes: size of DMA area * @dma_buffer_p: runtime dma buffer pointer + * @active_tasks: count of active tasks + * @total_tasks: count of all tasks + * @task_seqno: last task sequence number (!= 0) + * @tasks: list of all tasks */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -54,6 +82,13 @@ struct snd_compr_runtime { dma_addr_t dma_addr; size_t dma_bytes; struct snd_dma_buffer *dma_buffer_p; + +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + u32 active_tasks; + u32 total_tasks; + u64 task_seqno; + struct list_head tasks; +#endif }; /** @@ -108,6 +143,10 @@ struct snd_compr_stream { * Not valid if copy is implemented * @get_caps: Retrieve DSP capabilities, mandatory * @get_codec_caps: Retrieve capabilities for a specific codec, mandatory + * @task_create: Create a set of input/output buffers for accel operations + * @task_start: Start (queue) a task for accel operations + * @task_stop: Stop (dequeue) a task for accel operations + * @task_free: Free a set of input/output buffers for accel operations */ struct snd_compr_ops { int (*open)(struct snd_compr_stream *stream); @@ -132,6 +171,12 @@ struct snd_compr_ops { struct snd_compr_caps *caps); int (*get_codec_caps) (struct snd_compr_stream *stream, struct snd_compr_codec_caps *codec); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + int (*task_create) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task); + int (*task_start) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task); + int (*task_stop) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task); + int (*task_free) (struct snd_compr_stream *stream, struct snd_compr_task_runtime *task); +#endif }; /** @@ -242,4 +287,9 @@ int snd_compr_free_pages(struct snd_compr_stream *stream); int snd_compr_stop_error(struct snd_compr_stream *stream, snd_pcm_state_t state); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) +void snd_compr_task_finished(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task); +#endif + #endif diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index 5ff31e6d41c1..db1cc0b897fd 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -180,7 +180,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define SD_STS_FIFO_READY 0x20 /* FIFO ready */ /* INTCTL and INTSTS */ -#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define AZX_INT_ALL_STREAM 0x3fffffff /* all stream interrupts */ #define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ #define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0bf7d25434d7..67c99ffbf51b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -97,11 +97,11 @@ struct snd_pcm_ops { #define SNDRV_PCM_TRIGGER_STOP 0 #define SNDRV_PCM_TRIGGER_START 1 -#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3 -#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 -#define SNDRV_PCM_TRIGGER_SUSPEND 5 -#define SNDRV_PCM_TRIGGER_RESUME 6 -#define SNDRV_PCM_TRIGGER_DRAIN 7 +#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 2 +#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 3 +#define SNDRV_PCM_TRIGGER_SUSPEND 4 +#define SNDRV_PCM_TRIGGER_RESUME 5 +#define SNDRV_PCM_TRIGGER_DRAIN 6 #define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1) @@ -1393,30 +1393,6 @@ snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size); } -/** - * snd_pcm_mmap_data_open - increase the mmap counter - * @area: VMA - * - * PCM mmap callback should handle this counter properly - */ -static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) -{ - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; - atomic_inc(&substream->mmap_count); -} - -/** - * snd_pcm_mmap_data_close - decrease the mmap counter - * @area: VMA - * - * PCM mmap callback should handle this counter properly - */ -static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area) -{ - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; - atomic_dec(&substream->mmap_count); -} - int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area); /* mmap for io-memory area */ diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 8cd6da0480b7..72d2060904f6 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -156,6 +156,7 @@ struct tasdevice_priv { struct tasdevice_rca rcabin; struct calidata cali_data; struct tasdevice_fw *fmw; + struct gpio_desc *speaker_id; struct gpio_desc *reset; struct mutex codec_lock; struct regmap *regmap; diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index d185957f3fe0..d62eb93af0ed 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -14,7 +14,7 @@ #include <sound/compress_params.h> -#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 2, 0) +#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 3, 0) /** * struct snd_compressed_buffer - compressed buffer * @fragment_size: size of buffer fragment in bytes @@ -68,7 +68,8 @@ struct snd_compr_avail { enum snd_compr_direction { SND_COMPRESS_PLAYBACK = 0, - SND_COMPRESS_CAPTURE + SND_COMPRESS_CAPTURE, + SND_COMPRESS_ACCEL }; /** @@ -127,6 +128,59 @@ struct snd_compr_metadata { __u32 value[8]; } __attribute__((packed, aligned(4))); +/* flags for struct snd_compr_task */ +#define SND_COMPRESS_TFLG_NEW_STREAM (1<<0) /* mark for the new stream data */ + +/** + * struct snd_compr_task - task primitive for non-realtime operation + * @seqno: sequence number (task identifier) + * @origin_seqno: previous sequence number (task identifier) - for reuse + * @input_fd: data input file descriptor (dma-buf) + * @output_fd: data output file descriptor (dma-buf) + * @input_size: filled data in bytes (from caller, must not exceed fragment size) + * @flags: see SND_COMPRESS_TFLG_* defines + * @reserved: reserved for future extension + */ +struct snd_compr_task { + __u64 seqno; + __u64 origin_seqno; + int input_fd; + int output_fd; + __u64 input_size; + __u32 flags; + __u8 reserved[16]; +} __attribute__((packed, aligned(4))); + +/** + * enum snd_compr_state - task state + * @SND_COMPRESS_TASK_STATE_IDLE: task is not queued + * @SND_COMPRESS_TASK_STATE_ACTIVE: task is in the queue + * @SND_COMPRESS_TASK_STATE_FINISHED: task was processed, output is available + */ +enum snd_compr_state { + SND_COMPRESS_TASK_STATE_IDLE = 0, + SND_COMPRESS_TASK_STATE_ACTIVE, + SND_COMPRESS_TASK_STATE_FINISHED +}; + +/** + * struct snd_compr_task_status - task status + * @seqno: sequence number (task identifier) + * @input_size: filled data in bytes (from user space) + * @output_size: filled data in bytes (from driver) + * @output_flags: reserved for future (all zeros - from driver) + * @state: actual task state (SND_COMPRESS_TASK_STATE_*) + * @reserved: reserved for future extension + */ +struct snd_compr_task_status { + __u64 seqno; + __u64 input_size; + __u64 output_size; + __u32 output_flags; + __u8 state; + __u8 reserved[15]; +} __attribute__((packed, aligned(4))); + /* * compress path ioctl definitions * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP @@ -164,6 +218,14 @@ struct snd_compr_metadata { #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) #define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35) #define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36) + + +#define SNDRV_COMPRESS_TASK_CREATE _IOWR('C', 0x60, struct snd_compr_task) +#define SNDRV_COMPRESS_TASK_FREE _IOW('C', 0x61, __u64) +#define SNDRV_COMPRESS_TASK_START _IOWR('C', 0x62, struct snd_compr_task) +#define SNDRV_COMPRESS_TASK_STOP _IOW('C', 0x63, __u64) +#define SNDRV_COMPRESS_TASK_STATUS _IOWR('C', 0x68, struct snd_compr_task_status) + /* * TODO * 1. add mmap support diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 96d4d7eb879f..8dfffdc101a2 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -180,7 +180,7 @@ static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) /** * snd_ac97_codec_driver_register - register an AC97 codec driver - * @dev: AC97 driver codec to register + * @drv: AC97 driver codec to register * * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital * controller. @@ -196,7 +196,7 @@ EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register); /** * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver - * @dev: AC97 codec driver to unregister + * @drv: AC97 codec driver to unregister * * Unregister a previously registered ac97 codec driver. */ @@ -338,6 +338,7 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl) * @dev: the device providing the ac97 DC function * @slots_available: mask of the ac97 codecs that can be scanned and probed * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3 + * @codecs_pdata: codec platform data * * Register a digital controller which can control up to 4 ac97 codecs. This is * the controller side of the AC97 AC-link, while the slave side are the codecs. diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 4c367e73b2c9..77b11616a7ee 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -271,7 +271,7 @@ static void pxa2xx_ac97_remove(struct platform_device *dev) static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_probe, - .remove_new = pxa2xx_ac97_remove, + .remove = pxa2xx_ac97_remove, .driver = { .name = "pxa2xx-ac97", .pm = &pxa2xx_ac97_pm_ops, diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 402b5f66dcc3..d8f8e08f1bb7 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -861,7 +861,7 @@ static void atmel_ac97c_remove(struct platform_device *pdev) static struct platform_driver atmel_ac97c_driver = { .probe = atmel_ac97c_probe, - .remove_new = atmel_ac97c_remove, + .remove = atmel_ac97c_remove, .driver = { .name = "atmel_ac97c", .pm = ATMEL_AC97C_PM_OPS, diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 2c5b9f964703..48db44fa56fe 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -59,6 +59,9 @@ config SND_CORE_TEST config SND_COMPRESS_OFFLOAD tristate +config SND_COMPRESS_ACCEL + bool + config SND_JACK bool diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bdf1d78de833..86ed2fbee0c8 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -24,6 +24,7 @@ #include <linux/types.h> #include <linux/uio.h> #include <linux/uaccess.h> +#include <linux/dma-buf.h> #include <linux/module.h> #include <linux/compat.h> #include <sound/core.h> @@ -54,6 +55,12 @@ struct snd_compr_file { static void error_delayed_work(struct work_struct *work); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) +static void snd_compr_task_free_all(struct snd_compr_stream *stream); +#else +static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { } +#endif + /* * a note on stream states used: * we use following states in the compressed core @@ -85,6 +92,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) dirn = SND_COMPRESS_PLAYBACK; else if ((f->f_flags & O_ACCMODE) == O_RDONLY) dirn = SND_COMPRESS_CAPTURE; + else if ((f->f_flags & O_ACCMODE) == O_RDWR) + dirn = SND_COMPRESS_ACCEL; else return -EINVAL; @@ -125,6 +134,9 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + INIT_LIST_HEAD(&runtime->tasks); +#endif data->stream.runtime = runtime; f->private_data = (void *)data; scoped_guard(mutex, &compr->lock) @@ -154,6 +166,8 @@ static int snd_compr_free(struct inode *inode, struct file *f) break; } + snd_compr_task_free_all(&data->stream); + data->stream.ops->free(&data->stream); if (!data->stream.runtime->dma_buffer_p) kfree(data->stream.runtime->buffer); @@ -226,6 +240,9 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) struct snd_compr_avail ioctl_avail; size_t avail; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; + avail = snd_compr_calc_avail(stream, &ioctl_avail); ioctl_avail.avail = avail; @@ -287,6 +304,8 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, return -EFAULT; stream = &data->stream; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; guard(mutex)(&stream->device->lock); /* write is allowed when stream is running or has been setup */ switch (stream->runtime->state) { @@ -336,6 +355,8 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, return -EFAULT; stream = &data->stream; + if (stream->direction == SND_COMPRESS_ACCEL) + return -EBADFD; guard(mutex)(&stream->device->lock); /* read is allowed when stream is running, paused, draining and setup @@ -385,6 +406,7 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) { struct snd_compr_file *data = f->private_data; struct snd_compr_stream *stream; + struct snd_compr_runtime *runtime; size_t avail; __poll_t retval = 0; @@ -392,10 +414,11 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) return EPOLLERR; stream = &data->stream; + runtime = stream->runtime; guard(mutex)(&stream->device->lock); - switch (stream->runtime->state) { + switch (runtime->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_XRUN: return snd_compr_get_poll(stream) | EPOLLERR; @@ -403,23 +426,37 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) break; } - poll_wait(f, &stream->runtime->sleep, wait); + poll_wait(f, &runtime->sleep, wait); + +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + if (stream->direction == SND_COMPRESS_ACCEL) { + struct snd_compr_task_runtime *task; + if (runtime->fragments > runtime->active_tasks) + retval |= EPOLLOUT | EPOLLWRNORM; + task = list_first_entry_or_null(&runtime->tasks, + struct snd_compr_task_runtime, + list); + if (task && task->state == SND_COMPRESS_TASK_STATE_FINISHED) + retval |= EPOLLIN | EPOLLRDNORM; + return retval; + } +#endif avail = snd_compr_get_avail(stream); pr_debug("avail is %ld\n", (unsigned long)avail); /* check if we have at least one fragment to fill */ - switch (stream->runtime->state) { + switch (runtime->state) { case SNDRV_PCM_STATE_DRAINING: /* stream has been woken up after drain is complete * draining done so set stream state to stopped */ retval = snd_compr_get_poll(stream); - stream->runtime->state = SNDRV_PCM_STATE_SETUP; + runtime->state = SNDRV_PCM_STATE_SETUP; break; case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_PAUSED: - if (avail >= stream->runtime->fragment_size) + if (avail >= runtime->fragment_size) retval = snd_compr_get_poll(stream); break; default: @@ -521,6 +558,9 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, unsigned int buffer_size; void *buffer = NULL; + if (stream->direction == SND_COMPRESS_ACCEL) + goto params; + buffer_size = params->buffer.fragment_size * params->buffer.fragments; if (stream->ops->copy) { buffer = NULL; @@ -543,18 +583,30 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, if (!buffer) return -ENOMEM; } - stream->runtime->fragment_size = params->buffer.fragment_size; - stream->runtime->fragments = params->buffer.fragments; + stream->runtime->buffer = buffer; stream->runtime->buffer_size = buffer_size; +params: + stream->runtime->fragment_size = params->buffer.fragment_size; + stream->runtime->fragments = params->buffer.fragments; return 0; } -static int snd_compress_check_input(struct snd_compr_params *params) +static int +snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_params *params) { + u32 max_fragments; + /* first let's check the buffer parameter's */ - if (params->buffer.fragment_size == 0 || - params->buffer.fragments > U32_MAX / params->buffer.fragment_size || + if (params->buffer.fragment_size == 0) + return -EINVAL; + + if (stream->direction == SND_COMPRESS_ACCEL) + max_fragments = 64; /* safe value */ + else + max_fragments = U32_MAX / params->buffer.fragment_size; + + if (params->buffer.fragments > max_fragments || params->buffer.fragments == 0) return -EINVAL; @@ -583,7 +635,7 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) if (IS_ERR(params)) return PTR_ERR(params); - retval = snd_compress_check_input(params); + retval = snd_compress_check_input(stream, params); if (retval) return retval; @@ -939,6 +991,264 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) return snd_compress_wait_for_drain(stream); } +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + +static struct snd_compr_task_runtime * +snd_compr_find_task(struct snd_compr_stream *stream, __u64 seqno) +{ + struct snd_compr_task_runtime *task; + + list_for_each_entry(task, &stream->runtime->tasks, list) { + if (task->seqno == seqno) + return task; + } + return NULL; +} + +static void snd_compr_task_free(struct snd_compr_task_runtime *task) +{ + if (task->output) + dma_buf_put(task->output); + if (task->input) + dma_buf_put(task->input); + kfree(task); +} + +static u64 snd_compr_seqno_next(struct snd_compr_stream *stream) +{ + u64 seqno = ++stream->runtime->task_seqno; + if (seqno == 0) + seqno = ++stream->runtime->task_seqno; + return seqno; +} + +static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask) +{ + struct snd_compr_task_runtime *task; + int retval; + + if (stream->runtime->total_tasks >= stream->runtime->fragments) + return -EBUSY; + if (utask->origin_seqno != 0 || utask->input_size != 0) + return -EINVAL; + task = kzalloc(sizeof(*task), GFP_KERNEL); + if (task == NULL) + return -ENOMEM; + task->seqno = utask->seqno = snd_compr_seqno_next(stream); + task->input_size = utask->input_size; + retval = stream->ops->task_create(stream, task); + if (retval < 0) + goto cleanup; + utask->input_fd = dma_buf_fd(task->input, O_WRONLY|O_CLOEXEC); + if (utask->input_fd < 0) { + retval = utask->input_fd; + goto cleanup; + } + utask->output_fd = dma_buf_fd(task->output, O_RDONLY|O_CLOEXEC); + if (utask->output_fd < 0) { + retval = utask->output_fd; + goto cleanup; + } + /* keep dmabuf reference until freed with task free ioctl */ + dma_buf_get(utask->input_fd); + dma_buf_get(utask->output_fd); + list_add_tail(&task->list, &stream->runtime->tasks); + stream->runtime->total_tasks++; + return 0; +cleanup: + snd_compr_task_free(task); + return retval; +} + +static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task *task __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + task = memdup_user((void __user *)arg, sizeof(*task)); + if (IS_ERR(task)) + return PTR_ERR(no_free_ptr(task)); + retval = snd_compr_task_new(stream, task); + if (retval >= 0) + if (copy_to_user((void __user *)arg, task, sizeof(*task))) + retval = -EFAULT; + return retval; +} + +static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task, + struct snd_compr_task *utask) +{ + if (task == NULL) + return -EINVAL; + if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED) + return -EBUSY; + if (utask->input_size > task->input->size) + return -EINVAL; + task->flags = utask->flags; + task->input_size = utask->input_size; + task->state = SND_COMPRESS_TASK_STATE_IDLE; + return 0; +} + +static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_compr_task *utask) +{ + struct snd_compr_task_runtime *task; + int retval; + + if (utask->origin_seqno > 0) { + task = snd_compr_find_task(stream, utask->origin_seqno); + retval = snd_compr_task_start_prepare(task, utask); + if (retval < 0) + return retval; + task->seqno = utask->seqno = snd_compr_seqno_next(stream); + utask->origin_seqno = 0; + list_move_tail(&task->list, &stream->runtime->tasks); + } else { + task = snd_compr_find_task(stream, utask->seqno); + if (task && task->state != SND_COMPRESS_TASK_STATE_IDLE) + return -EBUSY; + retval = snd_compr_task_start_prepare(task, utask); + if (retval < 0) + return retval; + } + retval = stream->ops->task_start(stream, task); + if (retval >= 0) { + task->state = SND_COMPRESS_TASK_STATE_ACTIVE; + stream->runtime->active_tasks++; + } + return retval; +} + +static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task *task __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + task = memdup_user((void __user *)arg, sizeof(*task)); + if (IS_ERR(task)) + return PTR_ERR(no_free_ptr(task)); + retval = snd_compr_task_start(stream, task); + if (retval >= 0) + if (copy_to_user((void __user *)arg, task, sizeof(*task))) + retval = -EFAULT; + return retval; +} + +static void snd_compr_task_stop_one(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + if (task->state != SND_COMPRESS_TASK_STATE_ACTIVE) + return; + stream->ops->task_stop(stream, task); + if (!snd_BUG_ON(stream->runtime->active_tasks == 0)) + stream->runtime->active_tasks--; + list_move_tail(&task->list, &stream->runtime->tasks); + task->state = SND_COMPRESS_TASK_STATE_IDLE; +} + +static void snd_compr_task_free_one(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + snd_compr_task_stop_one(stream, task); + stream->ops->task_free(stream, task); + list_del(&task->list); + snd_compr_task_free(task); + stream->runtime->total_tasks--; +} + +static void snd_compr_task_free_all(struct snd_compr_stream *stream) +{ + struct snd_compr_task_runtime *task, *temp; + + list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list) + snd_compr_task_free_one(stream, task); +} + +typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task); + +static int snd_compr_task_seq(struct snd_compr_stream *stream, unsigned long arg, + snd_compr_seq_func_t fcn) +{ + struct snd_compr_task_runtime *task; + __u64 seqno; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + retval = get_user(seqno, (__u64 __user *)arg); + if (retval < 0) + return retval; + retval = 0; + if (seqno == 0) { + list_for_each_entry_reverse(task, &stream->runtime->tasks, list) + fcn(stream, task); + } else { + task = snd_compr_find_task(stream, seqno); + if (task == NULL) { + retval = -EINVAL; + } else { + fcn(stream, task); + } + } + return retval; +} + +static int snd_compr_task_status(struct snd_compr_stream *stream, + struct snd_compr_task_status *status) +{ + struct snd_compr_task_runtime *task; + + task = snd_compr_find_task(stream, status->seqno); + if (task == NULL) + return -EINVAL; + status->input_size = task->input_size; + status->output_size = task->output_size; + status->state = task->state; + return 0; +} + +static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_task_status *status __free(kfree) = NULL; + int retval; + + if (stream->runtime->state != SNDRV_PCM_STATE_SETUP) + return -EPERM; + status = memdup_user((void __user *)arg, sizeof(*status)); + if (IS_ERR(status)) + return PTR_ERR(no_free_ptr(status)); + retval = snd_compr_task_status(stream, status); + if (retval >= 0) + if (copy_to_user((void __user *)arg, status, sizeof(*status))) + retval = -EFAULT; + return retval; +} + +/** + * snd_compr_task_finished: Notify that the task was finished + * @stream: pointer to stream + * @task: runtime task structure + * + * Set the finished task state and notify waiters. + */ +void snd_compr_task_finished(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + guard(mutex)(&stream->device->lock); + if (!snd_BUG_ON(stream->runtime->active_tasks == 0)) + stream->runtime->active_tasks--; + task->state = SND_COMPRESS_TASK_STATE_FINISHED; + wake_up(&stream->runtime->sleep); +} +EXPORT_SYMBOL_GPL(snd_compr_task_finished); + +#endif /* CONFIG_SND_COMPRESS_ACCEL */ + static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { struct snd_compr_file *data = f->private_data; @@ -968,6 +1278,27 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) return snd_compr_set_metadata(stream, arg); case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): return snd_compr_get_metadata(stream, arg); + } + + if (stream->direction == SND_COMPRESS_ACCEL) { +#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_COMPRESS_TASK_CREATE): + return snd_compr_task_create(stream, arg); + case _IOC_NR(SNDRV_COMPRESS_TASK_FREE): + return snd_compr_task_seq(stream, arg, snd_compr_task_free_one); + case _IOC_NR(SNDRV_COMPRESS_TASK_START): + return snd_compr_task_start_ioctl(stream, arg); + case _IOC_NR(SNDRV_COMPRESS_TASK_STOP): + return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one); + case _IOC_NR(SNDRV_COMPRESS_TASK_STATUS): + return snd_compr_task_status_ioctl(stream, arg); + } +#endif + return -ENOTTY; + } + + switch (_IOC_NR(cmd)) { case _IOC_NR(SNDRV_COMPRESS_TSTAMP): return snd_compr_tstamp(stream, arg); case _IOC_NR(SNDRV_COMPRESS_AVAIL): @@ -1140,6 +1471,11 @@ int snd_compress_new(struct snd_card *card, int device, }; int ret; +#if !IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL) + if (snd_BUG_ON(dirn == SND_COMPRESS_ACCEL)) + return -EINVAL; +#endif + compr->card = card; compr->device = device; compr->direction = dirn; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b465fb6e1f5f..381a476a1045 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3774,6 +3774,26 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file #endif /* coherent mmap */ /* + * snd_pcm_mmap_data_open - increase the mmap counter + */ +static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ + struct snd_pcm_substream *substream = area->vm_private_data; + + atomic_inc(&substream->mmap_count); +} + +/* + * snd_pcm_mmap_data_close - decrease the mmap counter + */ +static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + struct snd_pcm_substream *substream = area->vm_private_data; + + atomic_dec(&substream->mmap_count); +} + +/* * fault callback for mmapping a RAM page */ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) @@ -3793,9 +3813,11 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) return VM_FAULT_SIGBUS; if (substream->ops->page) page = substream->ops->page(substream, offset); - else if (!snd_pcm_get_dma_buf(substream)) + else if (!snd_pcm_get_dma_buf(substream)) { + if (WARN_ON_ONCE(!runtime->dma_area)) + return VM_FAULT_SIGBUS; page = virt_to_page(runtime->dma_area + offset); - else + } else page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset); if (!page) return VM_FAULT_SIGBUS; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 03306be5fa02..348ce1b7725e 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -724,8 +724,9 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); if (!newbuf) return -ENOMEM; - guard(spinlock_irq)(&substream->lock); + spin_lock_irq(&substream->lock); if (runtime->buffer_ref) { + spin_unlock_irq(&substream->lock); kvfree(newbuf); return -EBUSY; } @@ -733,6 +734,7 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; __reset_runtime_ptrs(runtime, is_input); + spin_unlock_irq(&substream->lock); kvfree(oldbuf); } runtime->avail_min = params->avail_min; diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index bfed1a25fc8f..84e337ecbddd 100644 --- a/sound/core/sound_kunit.c +++ b/sound/core/sound_kunit.c @@ -172,6 +172,7 @@ static void test_format_fill_silence(struct kunit *test) u32 i, j; buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); for (i = 0; i < ARRAY_SIZE(buf_samples); i++) { for (j = 0; j < ARRAY_SIZE(valid_fmt); j++) @@ -208,8 +209,12 @@ static void test_playback_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) { r->buffer_size = p_avail_data[i].buffer_size; @@ -232,8 +237,12 @@ static void test_capture_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) { r->buffer_size = c_avail_data[i].buffer_size; @@ -247,6 +256,7 @@ static void test_capture_avail(struct kunit *test) static void test_card_set_id(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_card_set_id(card, VALID_NAME); KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME); @@ -280,6 +290,7 @@ static void test_pcm_format_name(struct kunit *test) static void test_card_add_component(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_component_add(card, TEST_FIRST_COMPONENT); KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT); diff --git a/sound/core/ump.c b/sound/core/ump.c index 7d59a0a9b037..6d0aac6c763f 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -366,7 +366,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, { struct snd_ump_block *fb, *p; - if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS) + if (blk >= SNDRV_UMP_MAX_BLOCKS) return -EINVAL; if (snd_ump_get_block(ump, blk)) @@ -387,7 +387,7 @@ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, fb->info.first_group = first_group; fb->info.num_groups = num_groups; /* fill the default name, may be overwritten to a better name */ - snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d", + snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u", first_group + 1, first_group + num_groups); /* put the entry in the ordered list */ @@ -788,7 +788,10 @@ static void fill_fb_info(struct snd_ump_endpoint *ump, info->ui_hint = buf->fb_info.ui_hint; info->first_group = buf->fb_info.first_group; info->num_groups = buf->fb_info.num_groups; - info->flags = buf->fb_info.midi_10; + if (buf->fb_info.midi_10 < 2) + info->flags = buf->fb_info.midi_10; + else + info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED; info->active = buf->fb_info.active; info->midi_ci_version = buf->fb_info.midi_ci_version; info->sysex8_streams = buf->fb_info.sysex8_streams; diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 6fc255a6754d..17f215bad0ec 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -1008,7 +1008,7 @@ static void snd_mts64_remove(struct platform_device *pdev) static struct platform_driver snd_mts64_driver = { .probe = snd_mts64_probe, - .remove_new = snd_mts64_remove, + .remove = snd_mts64_remove, .driver = { .name = PLATFORM_DRIVER, } diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 21cefaf5419a..72378f354fd0 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -640,7 +640,7 @@ static struct platform_device pcmtst_pdev = { static struct platform_driver pcmtst_pdrv = { .probe = pcmtst_probe, - .remove_new = pdev_remove, + .remove = pdev_remove, .driver = { .name = "pcmtest", }, diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 54d818d2f53d..5e4ef25a83a4 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -794,7 +794,7 @@ static void snd_portman_remove(struct platform_device *pdev) static struct platform_driver snd_portman_driver = { .probe = snd_portman_probe, - .remove_new = snd_portman_remove, + .remove = snd_portman_remove, .driver = { .name = PLATFORM_DRIVER, } diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index b596bec19774..f5028a061a91 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -333,53 +333,6 @@ retry_after_bus_reset: } EXPORT_SYMBOL(cmp_connection_establish); -/** - * cmp_connection_update - update the connection after a bus reset - * @c: the connection manager - * - * This function must be called from the driver's .update handler to - * reestablish any connection that might have been active. - * - * Returns zero on success, or a negative error code. On an error, the - * connection is broken and the caller must stop transmitting iso packets. - */ -int cmp_connection_update(struct cmp_connection *c) -{ - int err; - - mutex_lock(&c->mutex); - - if (!c->connected) { - mutex_unlock(&c->mutex); - return 0; - } - - err = fw_iso_resources_update(&c->resources); - if (err < 0) - goto err_unconnect; - - if (c->direction == CMP_OUTPUT) - err = pcr_modify(c, opcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); - else - err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); - - if (err < 0) - goto err_unconnect; - - mutex_unlock(&c->mutex); - - return 0; - -err_unconnect: - c->connected = false; - mutex_unlock(&c->mutex); - - return err; -} -EXPORT_SYMBOL(cmp_connection_update); - static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) { return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 26ab88000e34..66fc08b742d2 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -47,7 +47,6 @@ int cmp_connection_reserve(struct cmp_connection *connection, void cmp_connection_release(struct cmp_connection *connection); int cmp_connection_establish(struct cmp_connection *connection); -int cmp_connection_update(struct cmp_connection *connection); void cmp_connection_break(struct cmp_connection *connection); #endif diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index 3c26334227bb..991793e6bda9 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -886,7 +886,7 @@ static void hal2_remove(struct platform_device *pdev) static struct platform_driver hal2_driver = { .probe = hal2_probe, - .remove_new = hal2_remove, + .remove = hal2_remove, .driver = { .name = "sgihal2", } diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index a8551ccdd1bf..4e2ff954ff59 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -917,8 +917,8 @@ static void snd_sgio2audio_remove(struct platform_device *pdev) static struct platform_driver sgio2audio_driver = { .probe = snd_sgio2audio_probe, - .remove_new = snd_sgio2audio_remove, - .driver = { + .remove = snd_sgio2audio_remove, + .driver = { .name = "sgio2audio", } }; diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c index b8fad12f9e5f..8d443a3663d3 100644 --- a/sound/oss/dmasound/dmasound_paula.c +++ b/sound/oss/dmasound/dmasound_paula.c @@ -732,7 +732,7 @@ static void __exit amiga_audio_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver amiga_audio_driver __refdata = { - .remove_new = __exit_p(amiga_audio_remove), + .remove = __exit_p(amiga_audio_remove), .driver = { .name = "amiga-audio", }, diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index fdd4fe16225f..5a84591b13fc 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -464,7 +464,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, return -ENOMEM; } - err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, + hpi_stream_get_info_ex(dpcm->h_stream, NULL, &dpcm->hpi_buffer_attached, NULL, NULL, NULL); } bytes_per_sec = params_rate(params) * params_channels(params); diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7c6b1fe8dfcc..84393f4f429d 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -933,6 +933,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, bool match_all_pins) { const struct snd_hda_pin_quirk *pq; + const char *name = NULL; if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) return; @@ -946,9 +947,10 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; - codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->core.chip_name, codec->fixup_name); + name = pq->name; #endif + codec_info(codec, "%s: picked fixup %s (pin match)\n", + codec->core.chip_name, name ? name : ""); codec->fixup_list = fixlist; return; } @@ -956,6 +958,28 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); +/* check whether the given quirk entry matches with vendor/device pair */ +static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q) +{ + if (q->subvendor != vendor) + return false; + return !q->subdevice || + (device & q->subdevice_mask) == q->subdevice; +} + +/* look through the quirk list and return the matching entry */ +static const struct hda_quirk * +hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list) +{ + const struct hda_quirk *q; + + for (q = list; q->subvendor || q->subdevice; q++) { + if (hda_quirk_match(vendor, device, q)) + return q; + } + return NULL; +} + /** * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string * @codec: the HDA codec @@ -975,14 +999,16 @@ EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); */ void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, + const struct hda_quirk *quirk, const struct hda_fixup *fixlist) { - const struct snd_pci_quirk *q; + const struct hda_quirk *q; int id = HDA_FIXUP_ID_NOT_SET; const char *name = NULL; const char *type = NULL; unsigned int vendor, device; + u16 pci_vendor, pci_device; + u16 codec_vendor, codec_device; if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) return; @@ -991,8 +1017,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { id = HDA_FIXUP_ID_NO_FIXUP; fixlist = NULL; - codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->core.chip_name); + codec_info(codec, "%s: picked no fixup (nofixup specified)\n", + codec->core.chip_name); goto found; } @@ -1002,8 +1028,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (!strcmp(codec->modelname, models->name)) { id = models->id; name = models->name; - codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->core.chip_name, codec->fixup_name); + codec_info(codec, "%s: picked fixup %s (model specified)\n", + codec->core.chip_name, name); goto found; } models++; @@ -1013,27 +1039,42 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (!quirk) return; + if (codec->bus->pci) { + pci_vendor = codec->bus->pci->subsystem_vendor; + pci_device = codec->bus->pci->subsystem_device; + } + + codec_vendor = codec->core.subsystem_id >> 16; + codec_device = codec->core.subsystem_id & 0xffff; + /* match with the SSID alias given by the model string "XXXX:YYYY" */ if (codec->modelname && sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) { - q = snd_pci_quirk_lookup_id(vendor, device, quirk); + q = hda_quirk_lookup_id(vendor, device, quirk); if (q) { type = "alias SSID"; goto found_device; } } - /* match with the PCI SSID */ - q = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (q) { - type = "PCI SSID"; - goto found_device; + /* match primarily with the PCI SSID */ + for (q = quirk; q->subvendor || q->subdevice; q++) { + /* if the entry is specific to codec SSID, check with it */ + if (!codec->bus->pci || q->match_codec_ssid) { + if (hda_quirk_match(codec_vendor, codec_device, q)) { + type = "codec SSID"; + goto found_device; + } + } else { + if (hda_quirk_match(pci_vendor, pci_device, q)) { + type = "PCI SSID"; + goto found_device; + } + } } /* match with the codec SSID */ - q = snd_pci_quirk_lookup_id(codec->core.subsystem_id >> 16, - codec->core.subsystem_id & 0xffff, - quirk); + q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk); if (q) { type = "codec SSID"; goto found_device; @@ -1046,9 +1087,9 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; #endif - codec_dbg(codec, "%s: picked fixup %s for %s %04x:%04x\n", - codec->core.chip_name, name ? name : "", - type, q->subvendor, q->subdevice); + codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n", + codec->core.chip_name, name ? name : "", + type, q->subvendor, q->subdevice); found: codec->fixup_id = id; codec->fixup_list = fixlist; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b4540c5cd2a6..4a62440adfaf 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -773,6 +773,14 @@ static void azx_clear_irq_pending(struct azx *chip) static int azx_acquire_irq(struct azx *chip, int do_disconnect) { struct hdac_bus *bus = azx_bus(chip); + int ret; + + if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) { + ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX); + if (ret < 0) + return ret; + chip->msi = 0; + } if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, @@ -786,7 +794,6 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) } bus->irq = chip->pci->irq; chip->card->sync_irq = bus->irq; - pci_intx(chip->pci, !chip->msi); return 0; } @@ -1032,22 +1039,12 @@ static int azx_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hdac_bus *bus; if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - bus = azx_bus(chip); azx_shutdown_chip(chip); - if (bus->irq >= 0) { - free_irq(bus->irq, chip); - bus->irq = -1; - chip->card->sync_irq = -1; - } - - if (chip->msi) - pci_disable_msi(chip->pci); trace_azx_suspend(chip); return 0; @@ -1062,11 +1059,6 @@ static int __maybe_unused azx_resume(struct device *dev) return 0; chip = card->private_data; - if (chip->msi) - if (pci_enable_msi(chip->pci) < 0) - chip->msi = 0; - if (azx_acquire_irq(chip, 1) < 0) - return -EIO; __azx_runtime_resume(chip); @@ -1867,6 +1859,8 @@ static int azx_first_init(struct azx *chip) bus->polling_mode = 1; bus->not_use_interrupts = 1; bus->access_sdnctl_in_dword = 1; + if (!chip->jackpoll_interval) + chip->jackpoll_interval = msecs_to_jiffies(1500); } err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio"); @@ -1892,13 +1886,9 @@ static int azx_first_init(struct azx *chip) chip->gts_present = true; #endif - if (chip->msi) { - if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { - dev_dbg(card->dev, "Disabling 64bit MSI\n"); - pci->no_64bit_msi = true; - } - if (pci_enable_msi(pci) < 0) - chip->msi = 0; + if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) { + dev_dbg(card->dev, "Disabling 64bit MSI\n"); + pci->no_64bit_msi = true; } pci_set_master(pci); @@ -2050,7 +2040,7 @@ static int disable_msi_reset_irq(struct azx *chip) free_irq(bus->irq, chip); bus->irq = -1; chip->card->sync_irq = -1; - pci_disable_msi(chip->pci); + pci_free_irq_vectors(chip->pci); chip->msi = 0; err = azx_acquire_irq(chip, 1); if (err < 0) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 53a5a62b78fa..763f79f6f32e 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -292,6 +292,32 @@ struct hda_fixup { } v; }; +/* + * extended form of snd_pci_quirk: + * for PCI SSID matching, use SND_PCI_QUIRK() like before; + * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead + */ +struct hda_quirk { + unsigned short subvendor; /* PCI subvendor ID */ + unsigned short subdevice; /* PCI subdevice ID */ + unsigned short subdevice_mask; /* bitmask to match */ + bool match_codec_ssid; /* match only with codec SSID */ + int value; /* value */ +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *name; /* name of the device (optional) */ +#endif +}; + +#ifdef CONFIG_SND_DEBUG_VERBOSE +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\ + .match_codec_ssid = true } +#else +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \ + .match_codec_ssid = true } +#endif + struct snd_hda_pin_quirk { unsigned int codec; /* Codec vendor/device ID */ unsigned short subvendor; /* PCI subvendor ID */ @@ -351,7 +377,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action); void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth); void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, + const struct hda_quirk *quirk, const struct hda_fixup *fixlist); void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index d967e70a7058..b1e30a83dfb0 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -606,7 +606,7 @@ static struct platform_driver tegra_platform_hda = { .of_match_table = hda_tegra_match, }, .probe = hda_tegra_probe, - .remove_new = hda_tegra_remove, + .remove = hda_tegra_remove, .shutdown = hda_tegra_shutdown, }; module_platform_driver(tegra_platform_hda); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 1e9dadcdc51b..56354fe060a1 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -345,7 +345,7 @@ static const struct hda_fixup ad1986a_fixups[] = { }, }; -static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { +static const struct hda_quirk ad1986a_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC), SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD), @@ -588,7 +588,7 @@ static const struct hda_fixup ad1981_fixups[] = { }, }; -static const struct snd_pci_quirk ad1981_fixup_tbl[] = { +static const struct hda_quirk ad1981_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), @@ -1061,7 +1061,7 @@ static const struct hda_fixup ad1884_fixups[] = { }, }; -static const struct snd_pci_quirk ad1884_fixup_tbl[] = { +static const struct hda_quirk ad1884_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD), diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 654724559355..06e046214a41 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -385,7 +385,7 @@ static const struct hda_model_fixup cs420x_models[] = { {} }; -static const struct snd_pci_quirk cs420x_fixup_tbl[] = { +static const struct hda_quirk cs420x_fixup_tbl[] = { SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), @@ -634,13 +634,13 @@ static const struct hda_model_fixup cs4208_models[] = { {} }; -static const struct snd_pci_quirk cs4208_fixup_tbl[] = { +static const struct hda_quirk cs4208_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO), {} /* terminator */ }; /* codec SSID matching */ -static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { +static const struct hda_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), @@ -818,7 +818,7 @@ static const struct hda_model_fixup cs421x_models[] = { {} }; -static const struct snd_pci_quirk cs421x_fixup_tbl[] = { +static const struct hda_quirk cs421x_fixup_tbl[] = { /* Test Intel board + CDB2410 */ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), {} /* terminator */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b2bcdf76da30..2e9f817b948e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -828,23 +828,6 @@ static const struct hda_pintbl cxt_pincfg_sws_js201d[] = { {} }; -/* pincfg quirk for Tuxedo Sirius; - * unfortunately the (PCI) SSID conflicts with System76 Pangolin pang14, - * which has incompatible pin setup, so we check the codec SSID (luckily - * different one!) and conditionally apply the quirk here - */ -static void cxt_fixup_sirius_top_speaker(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* ignore for incorrectly picked-up pang14 */ - if (codec->core.subsystem_id == 0x278212b3) - return; - /* set up the top speaker pin */ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_codec_set_pincfg(codec, 0x1d, 0x82170111); -} - static const struct hda_fixup cxt_fixups[] = { [CXT_PINCFG_LENOVO_X200] = { .type = HDA_FIXUP_PINS, @@ -1009,12 +992,15 @@ static const struct hda_fixup cxt_fixups[] = { .v.pins = cxt_pincfg_sws_js201d, }, [CXT_PINCFG_TOP_SPEAKER] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_sirius_top_speaker, + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1d, 0x82170111 }, + { } + }, }, }; -static const struct snd_pci_quirk cxt5045_fixups[] = { +static const struct hda_quirk cxt5045_fixups[] = { SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105), /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have @@ -1034,7 +1020,7 @@ static const struct hda_model_fixup cxt5045_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5047_fixups[] = { +static const struct hda_quirk cxt5047_fixups[] = { /* HP laptops have really bad sound over 0 dB on NID 0x10. */ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047), @@ -1046,7 +1032,7 @@ static const struct hda_model_fixup cxt5047_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5051_fixups[] = { +static const struct hda_quirk cxt5051_fixups[] = { SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), {} @@ -1057,7 +1043,7 @@ static const struct hda_model_fixup cxt5051_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5066_fixups[] = { +static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC), SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC), @@ -1109,8 +1095,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), - SND_PCI_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), - SND_PCI_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER), + HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), + HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER), {} }; diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index 36b411d1a960..759f48038273 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -473,7 +473,7 @@ struct sub_codec dolphin_cs42l42_1 = { * Arrays Used for all projects using CS8409 ******************************************************************************/ -const struct snd_pci_quirk cs8409_fixup_tbl[] = { +const struct hda_quirk cs8409_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE), SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE), SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE), diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index 937e9387abdc..5e48115caf09 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -355,7 +355,7 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback; extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture; -extern const struct snd_pci_quirk cs8409_fixup_tbl[]; +extern const struct hda_quirk cs8409_fixup_tbl[]; extern const struct hda_model_fixup cs8409_models[]; extern const struct hda_fixup cs8409_fixups[]; extern const struct hda_verb cs8409_cs42l42_init_verbs[]; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 571fa8a6c9e1..2bf5c512ebaf 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -473,6 +473,8 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0234: case 0x10ec0274: + alc_write_coef_idx(codec, 0x6e, 0x0c25); + fallthrough; case 0x10ec0294: case 0x10ec0700: case 0x10ec0701: @@ -1565,7 +1567,7 @@ static const struct hda_fixup alc880_fixups[] = { }, }; -static const struct snd_pci_quirk alc880_fixup_tbl[] = { +static const struct hda_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), @@ -1874,7 +1876,7 @@ static const struct hda_fixup alc260_fixups[] = { }, }; -static const struct snd_pci_quirk alc260_fixup_tbl[] = { +static const struct hda_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), @@ -2566,7 +2568,7 @@ static const struct hda_fixup alc882_fixups[] = { }, }; -static const struct snd_pci_quirk alc882_fixup_tbl[] = { +static const struct hda_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), @@ -2910,7 +2912,7 @@ static const struct hda_fixup alc262_fixups[] = { }, }; -static const struct snd_pci_quirk alc262_fixup_tbl[] = { +static const struct hda_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), @@ -3071,7 +3073,7 @@ static const struct hda_model_fixup alc268_fixup_models[] = { {} }; -static const struct snd_pci_quirk alc268_fixup_tbl[] = { +static const struct hda_quirk alc268_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), /* below is codec SSID since multiple Toshiba laptops have the @@ -3613,25 +3615,22 @@ static void alc256_init(struct hda_codec *codec) hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); /* @@ -3655,29 +3654,28 @@ static void alc256_shutup(struct hda_codec *codec) alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); + msleep(75); /* 3k pull low control for Headset jack. */ /* NOTE: call this before clearing the pin, otherwise codec stalls */ /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly * when booting with headset plugged. So skip setting it for the codec alc257 */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + if (spec->en_3kpull_low) + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); @@ -3772,33 +3770,28 @@ static void alc225_init(struct hda_codec *codec) hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } } static void alc225_shutup(struct hda_codec *codec) @@ -3810,36 +3803,35 @@ static void alc225_shutup(struct hda_codec *codec) if (!hp_pin) hp_pin = 0x21; - alc_disable_headset_jack_key(codec); - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { + alc_disable_headset_jack_key(codec); + /* 3k pull low control for Headset jack. */ + alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); msleep(2); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_enable_headset_jack_key(codec); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); if (spec->ultra_low_power) { @@ -3850,9 +3842,6 @@ static void alc225_shutup(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); msleep(30); } - - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_enable_headset_jack_key(codec); } static void alc_default_init(struct hda_codec *codec) @@ -6502,6 +6491,16 @@ static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, } } +/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ +static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + } +} + /* Hook to update amp GPIO4 for automute */ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, struct hda_jack_callback *jack) @@ -7450,7 +7449,6 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action) { - alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */ switch (action) { case HDA_GEN_PCM_ACT_OPEN: alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ @@ -7464,7 +7462,6 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, static void alc287_s4_power_gpio3_default(struct hda_codec *codec) { if (is_s4_suspend(codec)) { - alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ } } @@ -7473,9 +7470,17 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; + static const struct coef_fw coefs[] = { + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + }; if (action != HDA_FIXUP_ACT_PRE_PROBE) return; + alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); + alc_process_coef_fw(codec, coefs); spec->power_hook = alc287_s4_power_gpio3_default; spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } @@ -7553,6 +7558,7 @@ enum { ALC269_FIXUP_THINKPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, + ALC269VC_FIXUP_INFINIX_Y4_MAX, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -7730,8 +7736,6 @@ enum { ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, ALC298_FIXUP_LENOVO_C940_DUET7, - ALC287_FIXUP_LENOVO_14IRP8_DUETITL, - ALC287_FIXUP_LENOVO_LEGION_7, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -7776,10 +7780,10 @@ 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, ALC245_FIXUP_CLEVO_NOISY_MIC, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, + ALC233_FIXUP_MEDION_MTL_SPK, + ALC294_FIXUP_BASS_SPEAKER_15, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -7799,72 +7803,6 @@ static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec, __snd_hda_apply_fixup(codec, id, action, 0); } -/* A special fixup for Lenovo Slim/Yoga Pro 9 14IRP8 and Yoga DuetITL 2021; - * 14IRP8 PCI SSID will mistakenly be matched with the DuetITL codec SSID, - * so we need to apply a different fixup in this case. The only DuetITL codec - * SSID reported so far is the 17aa:3802 while the 14IRP8 has the 17aa:38be - * and 17aa:38bf. If it weren't for the PCI SSID, the 14IRP8 models would - * have matched correctly by their codecs. - */ -static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - int id; - - if (codec->core.subsystem_id == 0x17aa3802) - id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* DuetITL */ - else - id = ALC287_FIXUP_TAS2781_I2C; /* 14IRP8 */ - __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 - */ -static void alc287_fixup_lenovo_legion_7(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - int id; - - if (codec->core.subsystem_id == 0x17aa38a8) - id = ALC287_FIXUP_TAS2781_I2C; /* Legion Pro 7 16ARX8H */ - else - id = ALC287_FIXUP_CS35L41_I2C_2; /* Legion 7i 16IAX7 */ - __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, @@ -8009,6 +7947,15 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* use as internal speaker */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -9804,14 +9751,6 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_lenovo_c940_duet7, }, - [ALC287_FIXUP_LENOVO_14IRP8_DUETITL] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_lenovo_14irp8_duetitl, - }, - [ALC287_FIXUP_LENOVO_LEGION_7] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_lenovo_legion_7, - }, [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -9996,10 +9935,6 @@ 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, @@ -10134,10 +10069,6 @@ 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, - }, [ALC245_FIXUP_CLEVO_NOISY_MIC] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, @@ -10154,9 +10085,20 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC233_FIXUP_MEDION_MTL_SPK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, + { } + }, + }, + [ALC294_FIXUP_BASS_SPEAKER_15] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc294_fixup_bass_speaker_15, + }, }; -static const struct snd_pci_quirk alc269_fixup_tbl[] = { +static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), @@ -10496,6 +10438,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), @@ -10578,6 +10521,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -10661,6 +10605,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), @@ -10888,11 +10833,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8 / DuetITL 2021", ALC287_FIXUP_LENOVO_14IRP8_DUETITL), + HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3820, "IdeaPad 330 / Yoga Duet 7", ALC287_FIXUP_LENOVO_SSID_17AA3820), + HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), @@ -10906,8 +10853,10 @@ 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 / Yoga Pro 7 14ARP8", ALC287_FIXUP_LENOVO_14ARP8_LEGION_IAH7), - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7/7i", ALC287_FIXUP_LENOVO_LEGION_7), + HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + HDA_CODEC_QUIRK(0x17aa, 0x386f, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), @@ -11018,7 +10967,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), + SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), @@ -11078,7 +11030,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { {} }; -static const struct snd_pci_quirk alc269_fixup_vendor_tbl[] = { +static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), @@ -11673,6 +11625,8 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { {0x1a, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, {0x19, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC, + {0x19, 0x40000000}), {} }; @@ -12012,7 +11966,7 @@ static const struct hda_fixup alc861_fixups[] = { } }; -static const struct snd_pci_quirk alc861_fixup_tbl[] = { +static const struct hda_quirk alc861_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), @@ -12116,7 +12070,7 @@ static const struct hda_fixup alc861vd_fixups[] = { }, }; -static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { +static const struct hda_quirk alc861vd_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), @@ -12917,7 +12871,7 @@ static const struct hda_fixup alc662_fixups[] = { }, }; -static const struct snd_pci_quirk alc662_fixup_tbl[] = { +static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ae1a34c68c61..bde6b7373858 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1462,7 +1462,7 @@ static const struct hda_model_fixup stac9200_models[] = { {} }; -static const struct snd_pci_quirk stac9200_fixup_tbl[] = { +static const struct hda_quirk stac9200_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), @@ -1683,7 +1683,7 @@ static const struct hda_model_fixup stac925x_models[] = { {} }; -static const struct snd_pci_quirk stac925x_fixup_tbl[] = { +static const struct hda_quirk stac925x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), @@ -1957,7 +1957,7 @@ static const struct hda_model_fixup stac92hd73xx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { +static const struct hda_quirk stac92hd73xx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), @@ -2753,7 +2753,7 @@ static const struct hda_model_fixup stac92hd83xxx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { +static const struct hda_quirk stac92hd83xxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD83XXX_REF), @@ -3236,7 +3236,7 @@ static const struct hda_model_fixup stac92hd71bxx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { +static const struct hda_quirk stac92hd71bxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), @@ -3496,7 +3496,7 @@ static const struct hda_pintbl ecs202_pin_configs[] = { }; /* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ -static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = { +static const struct hda_quirk stac922x_intel_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3), SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), @@ -3640,7 +3640,7 @@ static const struct hda_model_fixup stac922x_models[] = { {} }; -static const struct snd_pci_quirk stac922x_fixup_tbl[] = { +static const struct hda_quirk stac922x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D945_REF), @@ -3968,7 +3968,7 @@ static const struct hda_model_fixup stac927x_models[] = { {} }; -static const struct snd_pci_quirk stac927x_fixup_tbl[] = { +static const struct hda_quirk stac927x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D965_REF), @@ -4178,7 +4178,7 @@ static const struct hda_model_fixup stac9205_models[] = { {} }; -static const struct snd_pci_quirk stac9205_fixup_tbl[] = { +static const struct hda_quirk stac9205_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), @@ -4255,7 +4255,7 @@ static const struct hda_fixup stac92hd95_fixups[] = { }, }; -static const struct snd_pci_quirk stac92hd95_fixup_tbl[] = { +static const struct hda_quirk stac92hd95_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS), {} /* terminator */ }; @@ -5002,7 +5002,7 @@ static const struct hda_fixup stac9872_fixups[] = { }, }; -static const struct snd_pci_quirk stac9872_fixup_tbl[] = { +static const struct hda_quirk stac9872_fixup_tbl[] = { SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, "Sony VAIO F/S", STAC_9872_VAIO), {} /* terminator */ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a8ef4bb70dd0..d0893059b1b9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1035,7 +1035,7 @@ static const struct hda_fixup via_fixups[] = { }, }; -static const struct snd_pci_quirk vt2002p_fixups[] = { +static const struct hda_quirk vt2002p_fixups[] = { SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE), SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 370d847517f9..45cfb5a6f309 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -16,6 +16,7 @@ #include <linux/i2c.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pci_ids.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/hda_codec.h> @@ -110,10 +111,20 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) return 1; } +static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false }; + +static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = { + { "speakerid-gpios", &speakerid_gpios, 1 }, + { } +}; + static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct acpi_device *adev; + struct device *physdev; LIST_HEAD(resources); + const char *sub; + uint32_t subid; int ret; adev = acpi_dev_get_first_match_dev(hid, NULL, -1); @@ -123,18 +134,45 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) return -ENODEV; } + physdev = get_device(acpi_get_first_physical_node(adev)); ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); - if (ret < 0) + if (ret < 0) { + dev_err(p->dev, "Failed to get ACPI resource.\n"); + goto err; + } + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) { + dev_err(p->dev, "Failed to get SUBSYS ID.\n"); goto err; + } + /* Speaker id was needed for ASUS projects. */ + ret = kstrtou32(sub, 16, &subid); + if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) { + ret = devm_acpi_dev_add_driver_gpios(p->dev, + tas2781_speaker_id_gpios); + if (ret < 0) + dev_err(p->dev, "Failed to add driver gpio %d.\n", + ret); + p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN); + if (IS_ERR(p->speaker_id)) { + dev_err(p->dev, "Failed to get Speaker id.\n"); + ret = PTR_ERR(p->speaker_id); + goto err; + } + } else { + p->speaker_id = NULL; + } acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); + put_device(physdev); acpi_dev_put(adev); return 0; err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); + put_device(physdev); acpi_dev_put(adev); return ret; @@ -615,7 +653,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) struct tasdevice_priv *tas_priv = context; struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); struct hda_codec *codec = tas_priv->codec; - int i, ret; + int i, ret, spk_id; pm_runtime_get_sync(tas_priv->dev); mutex_lock(&tas_priv->codec_lock); @@ -648,8 +686,25 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tasdevice_dsp_remove(tas_priv); tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin", - codec->core.subsystem_id & 0xffff); + if (tas_priv->speaker_id != NULL) { + // Speaker id need to be checked for ASUS only. + spk_id = gpiod_get_value(tas_priv->speaker_id); + if (spk_id < 0) { + // Speaker id is not valid, use default. + dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id); + spk_id = 0; + } + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X%d.bin", + lower_16_bits(codec->core.subsystem_id), + spk_id); + } else { + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X.bin", + lower_16_bits(codec->core.subsystem_id)); + } ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 096ec76f5304..a12dafbf53ab 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -170,14 +170,9 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el tmp = stac9460_get(ice, idx); ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); - if (change) { - ovol = (0x7f - nvol) | (tmp & 0x80); - /* - dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n", - idx, ovol); - */ + if (change) stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); - } + return change; } diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 8e29c92830ad..f1b0cf9ea555 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -160,7 +160,7 @@ static SIMPLE_DEV_PM_OPS(snd_pmac_pm, snd_pmac_driver_suspend, snd_pmac_driver_r static struct platform_driver snd_pmac_driver = { .probe = snd_pmac_probe, - .remove_new = snd_pmac_remove, + .remove = snd_pmac_remove, .driver = { .name = SND_PMAC_DRIVER, .pm = SND_PMAC_PM_OPS, diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 936cd6e91529..39bf51ff43a1 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -315,8 +315,6 @@ static void aica_period_elapsed(struct timer_list *t) static void spu_begin_dma(struct snd_pcm_substream *substream) { struct snd_card_aica *dreamcastcard; - struct snd_pcm_runtime *runtime; - runtime = substream->runtime; dreamcastcard = substream->pcm->private_data; /*get the queue to do the work */ schedule_work(&(dreamcastcard->spu_dma_work)); @@ -601,7 +599,7 @@ static int snd_aica_probe(struct platform_device *devptr) static struct platform_driver snd_aica_driver = { .probe = snd_aica_probe, - .remove_new = snd_aica_remove, + .remove = snd_aica_remove, .driver = { .name = SND_AICA_DRIVER, }, diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index e7b6ce7bd086..e7b80328f0ef 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -383,7 +383,7 @@ probe_error: */ static struct platform_driver sh_dac_driver = { .probe = snd_sh_dac_probe, - .remove_new = snd_sh_dac_remove, + .remove = snd_sh_dac_remove, .driver = { .name = "dac_audio", }, diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index a1339f9ef12a..1b44119edfbc 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -2107,7 +2107,7 @@ static struct platform_driver cs4231_driver = { .of_match_table = cs4231_match, }, .probe = cs4231_probe, - .remove_new = cs4231_remove, + .remove = cs4231_remove, }; module_platform_driver(cs4231_driver); diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 050e98f32d36..69f1c9e37f4b 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2616,7 +2616,7 @@ static int dbri_probe(struct platform_device *op) strcpy(card->driver, "DBRI"); strcpy(card->shortname, "Sun DBRI"); rp = &op->resource[0]; - sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d", + sprintf(card->longname, "%s at 0x%02lx:0x%016llx, irq %d", card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, irq); @@ -2682,7 +2682,7 @@ static struct platform_driver dbri_sbus_driver = { .of_match_table = dbri_match, }, .probe = dbri_probe, - .remove_new = dbri_remove, + .remove = dbri_remove, }; module_platform_driver(dbri_sbus_driver); diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 33e962178c93..d562a30b087f 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -61,8 +61,10 @@ static void usb6fire_chip_abort(struct sfire_chip *chip) } } -static void usb6fire_chip_destroy(struct sfire_chip *chip) +static void usb6fire_card_free(struct snd_card *card) { + struct sfire_chip *chip = card->private_data; + if (chip) { if (chip->pcm) usb6fire_pcm_destroy(chip); @@ -72,8 +74,6 @@ static void usb6fire_chip_destroy(struct sfire_chip *chip) usb6fire_comm_destroy(chip); if (chip->control) usb6fire_control_destroy(chip); - if (chip->card) - snd_card_free(chip->card); } } @@ -136,6 +136,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf, chip->regidx = regidx; chip->intf_count = 1; chip->card = card; + card->private_free = usb6fire_card_free; ret = usb6fire_comm_init(chip); if (ret < 0) @@ -162,7 +163,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf, return 0; destroy_chip: - usb6fire_chip_destroy(chip); + snd_card_free(card); return ret; } @@ -181,7 +182,6 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf) chip->shutdown = true; usb6fire_chip_abort(chip); - usb6fire_chip_destroy(chip); } } } diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 772c0ecb7077..05f964347ed6 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -858,14 +858,20 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) return 0; } -void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev) +void snd_usb_caiaq_audio_disconnect(struct snd_usb_caiaqdev *cdev) { struct device *dev = caiaqdev_to_dev(cdev); dev_dbg(dev, "%s(%p)\n", __func__, cdev); stream_stop(cdev); +} + +void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev) +{ + struct device *dev = caiaqdev_to_dev(cdev); + + dev_dbg(dev, "%s(%p)\n", __func__, cdev); free_urbs(cdev->data_urbs_in); free_urbs(cdev->data_urbs_out); kfree(cdev->data_cb_info); } - diff --git a/sound/usb/caiaq/audio.h b/sound/usb/caiaq/audio.h index 869bf6264d6a..07f5d064456c 100644 --- a/sound/usb/caiaq/audio.h +++ b/sound/usb/caiaq/audio.h @@ -3,6 +3,7 @@ #define CAIAQ_AUDIO_H int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev); +void snd_usb_caiaq_audio_disconnect(struct snd_usb_caiaqdev *cdev); void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev); #endif /* CAIAQ_AUDIO_H */ diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index b5cbf1f195c4..dfd820483849 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -376,6 +376,17 @@ static void setup_card(struct snd_usb_caiaqdev *cdev) dev_err(dev, "Unable to set up control system (ret=%d)\n", ret); } +static void card_free(struct snd_card *card) +{ + struct snd_usb_caiaqdev *cdev = caiaqdev(card); + +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + snd_usb_caiaq_input_free(cdev); +#endif + snd_usb_caiaq_audio_free(cdev); + usb_reset_device(cdev->chip.dev); +} + static int create_card(struct usb_device *usb_dev, struct usb_interface *intf, struct snd_card **cardp) @@ -489,6 +500,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev) cdev->vendor_name, cdev->product_name, usbpath); setup_card(cdev); + card->private_free = card_free; return 0; err_kill_urb: @@ -534,15 +546,14 @@ static void snd_disconnect(struct usb_interface *intf) snd_card_disconnect(card); #ifdef CONFIG_SND_USB_CAIAQ_INPUT - snd_usb_caiaq_input_free(cdev); + snd_usb_caiaq_input_disconnect(cdev); #endif - snd_usb_caiaq_audio_free(cdev); + snd_usb_caiaq_audio_disconnect(cdev); usb_kill_urb(&cdev->ep1_in_urb); usb_kill_urb(&cdev->midi_out_urb); - snd_card_free(card); - usb_reset_device(interface_to_usbdev(intf)); + snd_card_free_when_closed(card); } diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 84f26dce7f5d..a9130891bb69 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -829,15 +829,21 @@ exit_free_idev: return ret; } -void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev) +void snd_usb_caiaq_input_disconnect(struct snd_usb_caiaqdev *cdev) { if (!cdev || !cdev->input_dev) return; usb_kill_urb(cdev->ep4_in_urb); + input_unregister_device(cdev->input_dev); +} + +void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev) +{ + if (!cdev || !cdev->input_dev) + return; + usb_free_urb(cdev->ep4_in_urb); cdev->ep4_in_urb = NULL; - - input_unregister_device(cdev->input_dev); cdev->input_dev = NULL; } diff --git a/sound/usb/caiaq/input.h b/sound/usb/caiaq/input.h index c42891e7be88..fbe267f85d02 100644 --- a/sound/usb/caiaq/input.h +++ b/sound/usb/caiaq/input.h @@ -4,6 +4,7 @@ void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev, char *buf, unsigned int len); int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev); +void snd_usb_caiaq_input_disconnect(struct snd_usb_caiaqdev *cdev); void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev); #endif diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 8f85200292f3..842ba5b801ea 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -36,6 +36,12 @@ union uac23_clock_multiplier_desc { struct uac_clock_multiplier_descriptor v3; }; +/* check whether the descriptor bLength has the minimal length */ +#define DESC_LENGTH_CHECK(p, proto) \ + ((proto) == UAC_VERSION_3 ? \ + ((p)->v3.bLength >= sizeof((p)->v3)) : \ + ((p)->v2.bLength >= sizeof((p)->v2))) + #define GET_VAL(p, proto, field) \ ((proto) == UAC_VERSION_3 ? (p)->v3.field : (p)->v2.field) @@ -58,6 +64,8 @@ static bool validate_clock_source(void *p, int id, int proto) { union uac23_clock_source_desc *cs = p; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; return GET_VAL(cs, proto, bClockID) == id; } @@ -65,13 +73,27 @@ static bool validate_clock_selector(void *p, int id, int proto) { union uac23_clock_selector_desc *cs = p; - return GET_VAL(cs, proto, bClockID) == id; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; + if (GET_VAL(cs, proto, bClockID) != id) + return false; + /* additional length check for baCSourceID array (in bNrInPins size) + * and two more fields (which sizes depend on the protocol) + */ + if (proto == UAC_VERSION_3) + return cs->v3.bLength >= sizeof(cs->v3) + cs->v3.bNrInPins + + 4 /* bmControls */ + 2 /* wCSelectorDescrStr */; + else + return cs->v2.bLength >= sizeof(cs->v2) + cs->v2.bNrInPins + + 1 /* bmControls */ + 1 /* iClockSelector */; } static bool validate_clock_multiplier(void *p, int id, int proto) { union uac23_clock_multiplier_desc *cs = p; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; return GET_VAL(cs, proto, bClockID) == id; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bd67027c7677..66976be06bc0 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1084,6 +1084,21 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { struct snd_usb_audio *chip = cval->head.mixer->chip; + + if (chip->quirk_flags & QUIRK_FLAG_MIC_RES_384) { + if (!strcmp(kctl->id.name, "Mic Capture Volume")) { + usb_audio_info(chip, + "set resolution quirk: cval->res = 384\n"); + cval->res = 384; + } + } else if (chip->quirk_flags & QUIRK_FLAG_MIC_RES_16) { + if (!strcmp(kctl->id.name, "Mic Capture Volume")) { + usb_audio_info(chip, + "set resolution quirk: cval->res = 16\n"); + cval->res = 16; + } + } + switch (chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ @@ -1168,27 +1183,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, } break; - case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */ - case USB_ID(0x046d, 0x0808): - case USB_ID(0x046d, 0x0809): - case USB_ID(0x046d, 0x0819): /* Logitech Webcam C210 */ - case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */ - case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ - case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ - case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ - case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */ - case USB_ID(0x046d, 0x0991): - case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */ - /* Most audio usb devices lie about volume resolution. - * Most Logitech webcams have res = 384. - * Probably there is some logitech magic behind this number --fishor - */ - if (!strcmp(kctl->id.name, "Mic Capture Volume")) { - usb_audio_info(chip, - "set resolution quirk: cval->res = 384\n"); - cval->res = 384; - } - break; case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */ if ((strstr(kctl->id.name, "Playback Volume") != NULL) || strstr(kctl->id.name, "Capture Volume") != NULL) { @@ -1197,28 +1191,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->res = 1; } break; - case USB_ID(0x1224, 0x2a25): /* Jieli Technology USB PHY 2.0 */ - if (!strcmp(kctl->id.name, "Mic Capture Volume")) { - usb_audio_info(chip, - "set resolution quirk: cval->res = 16\n"); - cval->res = 16; - } - break; - case USB_ID(0x1bcf, 0x2283): /* NexiGo N930AF FHD Webcam */ - case USB_ID(0x03f0, 0x654a): /* HP 320 FHD Webcam */ - if (!strcmp(kctl->id.name, "Mic Capture Volume")) { - usb_audio_info(chip, - "set resolution quirk: cval->res = 16\n"); - cval->res = 16; - } - break; - case USB_ID(0x1bcf, 0x2281): /* HD Webcam */ - if (!strcmp(kctl->id.name, "Mic Capture Volume")) { - usb_audio_info(chip, - "set resolution quirk: cval->res = 16\n"); - cval->res = 16; - } - break; } } @@ -2225,7 +2197,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state, len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (!len) - len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); + snprintf(kctl->id.name, sizeof(kctl->id.name), "Mixer Source %d", in_ch + 1); + append_ctl_name(kctl, " Volume"); usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n", diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 6456e87e2f39..8bbf070b3676 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3498,7 +3498,7 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) } /* - * Pioneer DJ DJM Mixers + * Pioneer DJ / AlphaTheta DJM Mixers * * These devices generally have options for soft-switching the playback and * capture sources in addition to the recording level. Although different @@ -3515,17 +3515,23 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_CAP_CDLINE 0x01 #define SND_DJM_CAP_DIGITAL 0x02 #define SND_DJM_CAP_PHONO 0x03 +#define SND_DJM_CAP_PREFADER 0x05 #define SND_DJM_CAP_PFADER 0x06 #define SND_DJM_CAP_XFADERA 0x07 #define SND_DJM_CAP_XFADERB 0x08 #define SND_DJM_CAP_MIC 0x09 #define SND_DJM_CAP_AUX 0x0d #define SND_DJM_CAP_RECOUT 0x0a +#define SND_DJM_CAP_RECOUT_NOMIC 0x0e #define SND_DJM_CAP_NONE 0x0f #define SND_DJM_CAP_CH1PFADER 0x11 #define SND_DJM_CAP_CH2PFADER 0x12 #define SND_DJM_CAP_CH3PFADER 0x13 #define SND_DJM_CAP_CH4PFADER 0x14 +#define SND_DJM_CAP_CH1PREFADER 0x31 +#define SND_DJM_CAP_CH2PREFADER 0x32 +#define SND_DJM_CAP_CH3PREFADER 0x33 +#define SND_DJM_CAP_CH4PREFADER 0x34 // Playback types #define SND_DJM_PB_CH1 0x00 @@ -3551,6 +3557,7 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_900NXS2_IDX 0x3 #define SND_DJM_750MK2_IDX 0x4 #define SND_DJM_450_IDX 0x5 +#define SND_DJM_A9_IDX 0x6 #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ @@ -3579,7 +3586,7 @@ struct snd_djm_ctl { u16 wIndex; }; -static const char *snd_djm_get_label_caplevel(u16 wvalue) +static const char *snd_djm_get_label_caplevel_common(u16 wvalue) { switch (wvalue) { case 0x0000: return "-19dB"; @@ -3590,6 +3597,20 @@ static const char *snd_djm_get_label_caplevel(u16 wvalue) } }; +// The DJM-A9 has different capture levels than other, older models +static const char *snd_djm_get_label_caplevel_a9(u16 wvalue) +{ + switch (wvalue) { + case 0x0000: return "+15dB"; + case 0x0100: return "+12dB"; + case 0x0200: return "+9dB"; + case 0x0300: return "+6dB"; + case 0x0400: return "+3dB"; + case 0x0500: return "0dB"; + default: return NULL; + } +}; + static const char *snd_djm_get_label_cap_common(u16 wvalue) { switch (wvalue & 0x00ff) { @@ -3602,8 +3623,13 @@ static const char *snd_djm_get_label_cap_common(u16 wvalue) case SND_DJM_CAP_XFADERB: return "Cross Fader B"; case SND_DJM_CAP_MIC: return "Mic"; case SND_DJM_CAP_RECOUT: return "Rec Out"; + case SND_DJM_CAP_RECOUT_NOMIC: return "Rec Out without Mic"; case SND_DJM_CAP_AUX: return "Aux"; case SND_DJM_CAP_NONE: return "None"; + case SND_DJM_CAP_CH1PREFADER: return "Pre Fader Ch1"; + case SND_DJM_CAP_CH2PREFADER: return "Pre Fader Ch2"; + case SND_DJM_CAP_CH3PREFADER: return "Pre Fader Ch3"; + case SND_DJM_CAP_CH4PREFADER: return "Pre Fader Ch4"; case SND_DJM_CAP_CH1PFADER: return "Post Fader Ch1"; case SND_DJM_CAP_CH2PFADER: return "Post Fader Ch2"; case SND_DJM_CAP_CH3PFADER: return "Post Fader Ch3"; @@ -3623,6 +3649,14 @@ static const char *snd_djm_get_label_cap_850(u16 wvalue) } }; +static const char *snd_djm_get_label_caplevel(u8 device_idx, u16 wvalue) +{ + switch (device_idx) { + case SND_DJM_A9_IDX: return snd_djm_get_label_caplevel_a9(wvalue); + default: return snd_djm_get_label_caplevel_common(wvalue); + } +}; + static const char *snd_djm_get_label_cap(u8 device_idx, u16 wvalue) { switch (device_idx) { @@ -3644,7 +3678,7 @@ static const char *snd_djm_get_label_pb(u16 wvalue) static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex) { switch (windex) { - case SND_DJM_WINDEX_CAPLVL: return snd_djm_get_label_caplevel(wvalue); + case SND_DJM_WINDEX_CAPLVL: return snd_djm_get_label_caplevel(device_idx, wvalue); case SND_DJM_WINDEX_CAP: return snd_djm_get_label_cap(device_idx, wvalue); case SND_DJM_WINDEX_PB: return snd_djm_get_label_pb(wvalue); default: return NULL; @@ -3653,7 +3687,7 @@ static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex) // common DJM capture level option values static const u16 snd_djm_opts_cap_level[] = { - 0x0000, 0x0100, 0x0200, 0x0300 }; + 0x0000, 0x0100, 0x0200, 0x0300, 0x400, 0x500 }; // DJM-250MK2 @@ -3795,6 +3829,28 @@ static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { }; +// DJM-A9 +static const u16 snd_djm_opts_a9_cap1[] = { + 0x0107, 0x0108, 0x0109, 0x010a, 0x010e, + 0x111, 0x112, 0x113, 0x114, 0x0131, 0x132, 0x133, 0x134 }; +static const u16 snd_djm_opts_a9_cap2[] = { + 0x0201, 0x0202, 0x0203, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020e }; +static const u16 snd_djm_opts_a9_cap3[] = { + 0x0301, 0x0302, 0x0303, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030e }; +static const u16 snd_djm_opts_a9_cap4[] = { + 0x0401, 0x0402, 0x0403, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040e }; +static const u16 snd_djm_opts_a9_cap5[] = { + 0x0501, 0x0502, 0x0503, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050a, 0x050e }; + +static const struct snd_djm_ctl snd_djm_ctls_a9[] = { + SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL), + SND_DJM_CTL("Master Input", a9_cap1, 3, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch1 Input", a9_cap2, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch2 Input", a9_cap3, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch3 Input", a9_cap4, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch4 Input", a9_cap5, 2, SND_DJM_WINDEX_CAP) +}; + static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2), [SND_DJM_750_IDX] = SND_DJM_DEVICE(750), @@ -3802,6 +3858,7 @@ static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_900NXS2_IDX] = SND_DJM_DEVICE(900nxs2), [SND_DJM_750MK2_IDX] = SND_DJM_DEVICE(750mk2), [SND_DJM_450_IDX] = SND_DJM_DEVICE(450), + [SND_DJM_A9_IDX] = SND_DJM_DEVICE(a9), }; @@ -4079,6 +4136,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ err = snd_djm_controls_create(mixer, SND_DJM_900NXS2_IDX); break; + case USB_ID(0x2b73, 0x003c): /* Pioneer DJ / AlphaTheta DJM-A9 */ + err = snd_djm_controls_create(mixer, SND_DJM_A9_IDX); + break; } return err; diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 4cddf84db631..7f595c1752a5 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -11,7 +11,7 @@ * - Clarett 2Pre/4Pre/8Pre USB * - Clarett+ 2Pre/4Pre/8Pre * - * Copyright (c) 2018-2023 by Geoffrey D. Bennett <g at b4.vu> + * Copyright (c) 2018-2024 by Geoffrey D. Bennett <g at b4.vu> * Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com> * Copyright (c) 2022 by Christian Colglazier <christian@cacolglazier.com> * @@ -1079,6 +1079,9 @@ struct scarlett2_device_info { /* minimum firmware version required */ u16 min_firmware_version; + /* has a downloadable device map */ + u8 has_devmap; + /* support for main/alt speaker switching */ u8 has_speaker_switching; @@ -1253,7 +1256,7 @@ struct scarlett2_data { u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; u8 input_select_switch; - u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX / 2]; + u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 gain[SCARLETT2_INPUT_GAIN_MAX]; u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX]; @@ -1284,7 +1287,7 @@ struct scarlett2_data { struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX]; struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; struct snd_kcontrol *input_select_ctl; - struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2]; + struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX]; @@ -1773,6 +1776,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { static const struct scarlett2_device_info vocaster_one_info = { .config_set = &scarlett2_config_set_vocaster, .min_firmware_version = 1769, + .has_devmap = 1, .phantom_count = 1, .inputs_per_phantom = 1, @@ -1815,6 +1819,7 @@ static const struct scarlett2_device_info vocaster_one_info = { static const struct scarlett2_device_info vocaster_two_info = { .config_set = &scarlett2_config_set_vocaster, .min_firmware_version = 1769, + .has_devmap = 1, .phantom_count = 2, .inputs_per_phantom = 1, @@ -1858,6 +1863,7 @@ static const struct scarlett2_device_info vocaster_two_info = { static const struct scarlett2_device_info solo_gen4_info = { .config_set = &scarlett2_config_set_gen4_solo, .min_firmware_version = 2115, + .has_devmap = 1, .level_input_count = 1, .air_input_count = 1, @@ -1912,6 +1918,7 @@ static const struct scarlett2_device_info solo_gen4_info = { static const struct scarlett2_device_info s2i2_gen4_info = { .config_set = &scarlett2_config_set_gen4_2i2, .min_firmware_version = 2115, + .has_devmap = 1, .level_input_count = 2, .air_input_count = 2, @@ -1966,6 +1973,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = { static const struct scarlett2_device_info s4i4_gen4_info = { .config_set = &scarlett2_config_set_gen4_4i4, .min_firmware_version = 2089, + .has_devmap = 1, .level_input_count = 2, .air_input_count = 2, @@ -2264,6 +2272,8 @@ static int scarlett2_get_port_start_num( #define SCARLETT2_USB_GET_DATA 0x00800000 #define SCARLETT2_USB_SET_DATA 0x00800001 #define SCARLETT2_USB_DATA_CMD 0x00800002 +#define SCARLETT2_USB_INFO_DEVMAP 0x0080000c +#define SCARLETT2_USB_GET_DEVMAP 0x0080000d #define SCARLETT2_USB_CONFIG_SAVE 6 @@ -2277,6 +2287,14 @@ static int scarlett2_get_port_start_num( #define SCARLETT2_SEGMENT_SETTINGS_NAME "App_Settings" #define SCARLETT2_SEGMENT_FIRMWARE_NAME "App_Upgrade" +/* Gen 4 device firmware provides access to a base64-encoded + * zlib-compressed JSON description of the device's capabilities and + * configuration. This device map is made available in + * /proc/asound/cardX/device-map.json.zz.b64 + */ +#define SCARLETT2_DEVMAP_BLOCK_SIZE 1024 +#define SCARLETT2_DEVMAP_FILENAME "device-map.json.zz.b64" + /* proprietary request/response format */ struct scarlett2_usb_packet { __le32 cmd; @@ -3409,7 +3427,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) private->num_autogain_status_texts - 1; - for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) if (scarlett2_has_config_item(private, scarlett2_ag_target_configs[i])) { err = scarlett2_usb_get_config( @@ -3420,7 +3438,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) } /* convert from negative dBFS as used by the device */ - for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) + for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++) private->ag_targets[i] = -ag_target_values[i]; return 0; @@ -3439,7 +3457,7 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) scarlett2_set_ctl_access(private->input_select_ctl, val); if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) - for (i = 0; i < info->gain_input_count / 2; i++) + for (i = 0; i < info->gain_input_count; i++) scarlett2_set_ctl_access(private->input_link_ctls[i], val); for (i = 0; i < info->gain_input_count; i++) @@ -3480,7 +3498,7 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) &private->input_select_ctl->id); if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) - for (i = 0; i < info->gain_input_count / 2; i++) + for (i = 0; i < info->gain_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->input_link_ctls[i]->id); for (i = 0; i < info->gain_input_count; i++) @@ -3825,7 +3843,7 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - int link_count = info->gain_input_count / 2; + int link_count = info->gain_input_count; int err; private->input_select_updated = 0; @@ -3847,10 +3865,6 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer) if (err < 0) return err; - /* simplified because no model yet has link_count > 1 */ - if (private->input_link_switch[0]) - private->input_select_switch = 0; - return 0; } @@ -3887,9 +3901,9 @@ static int scarlett2_input_select_ctl_put( struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; int oval, val, err; - int max_val = private->input_link_switch[0] ? 0 : 1; mutex_lock(&private->data_mutex); @@ -3907,19 +3921,18 @@ static int scarlett2_input_select_ctl_put( if (val < 0) val = 0; - else if (val > max_val) - val = max_val; + else if (val >= info->gain_input_count) + val = info->gain_input_count - 1; if (oval == val) goto unlock; private->input_select_switch = val; - /* Send switch change to the device if inputs not linked */ - if (!private->input_link_switch[0]) - err = scarlett2_usb_set_config( - mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, - 1, val); + /* Send new value to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, + 0, val); if (err == 0) err = 1; @@ -3936,8 +3949,7 @@ static int scarlett2_input_select_ctl_info( struct scarlett2_data *private = mixer->private_data; int inputs = private->info->gain_input_count; - int i, j; - int err; + int i, err; char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL); if (!values) @@ -3954,21 +3966,11 @@ static int scarlett2_input_select_ctl_info( if (err < 0) goto unlock; - /* Loop through each input - * Linked inputs have one value for the pair - */ - for (i = 0, j = 0; i < inputs; i++) { - if (private->input_link_switch[i / 2]) { - values[j++] = kasprintf( - GFP_KERNEL, "Input %d-%d", i + 1, i + 2); - i++; - } else { - values[j++] = kasprintf( - GFP_KERNEL, "Input %d", i + 1); - } - } + /* Loop through each input */ + for (i = 0; i < inputs; i++) + values[i] = kasprintf(GFP_KERNEL, "Input %d", i + 1); - err = snd_ctl_enum_info(uinfo, 1, j, + err = snd_ctl_enum_info(uinfo, 1, i, (const char * const *)values); unlock: @@ -4077,18 +4079,8 @@ static int scarlett2_input_link_ctl_put( private->input_link_switch[index] = val; - /* Notify of change in input select options available */ - snd_ctl_notify(mixer->chip->card, - SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, - &private->input_select_ctl->id); - private->input_select_updated = 1; - - /* Send switch change to the device - * Link for channels 1-2 is at index 1 - * No device yet has more than 2 channels linked - */ err = scarlett2_usb_set_config( - mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index + 1, val); + mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index, val); if (err == 0) err = 1; @@ -5385,6 +5377,8 @@ static int scarlett2_compressor_ctl_put( int index = elem->control; int channel = index / SCARLETT2_COMPRESSOR_PARAM_COUNT; int param_index = index % SCARLETT2_COMPRESSOR_PARAM_COUNT; + const struct compressor_param *param = &compressor_params[param_index]; + int oval, val, err; s32 scaled_val; @@ -5406,8 +5400,6 @@ static int scarlett2_compressor_ctl_put( private->compressor_values[index] = val; - const struct compressor_param *param = &compressor_params[param_index]; - scaled_val = val << param->scale_bits; /* Send change to the device */ @@ -6916,10 +6908,9 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) { - for (i = 0; i < info->gain_input_count / 2; i++) { + for (i = 0; i < info->gain_input_count; i++) { scnprintf(s, sizeof(s), - "Line In %d-%d Link Capture Switch", - (i * 2) + 1, (i * 2) + 2); + "Line In %d Link Capture Switch", i + 1); err = scarlett2_add_new_ctl( mixer, &scarlett2_input_link_ctl, i, 1, s, &private->input_link_ctls[i]); @@ -8246,7 +8237,7 @@ static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer) SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &private->input_select_ctl->id); - for (i = 0; i < info->gain_input_count / 2; i++) + for (i = 0; i < info->gain_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->input_link_ctls[i]->id); } @@ -9518,7 +9509,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw, SCARLETT2_FLASH_BLOCK_SIZE; if (count < 0 || *offset < 0 || *offset + count >= flash_size) - return -EINVAL; + return -ENOSPC; if (!count) return 0; @@ -9591,6 +9582,116 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer) return 0; } +/*** device-map file ***/ + +static ssize_t scarlett2_devmap_read( + struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, + size_t count, + loff_t pos) +{ + struct usb_mixer_interface *mixer = entry->private_data; + u8 *resp_buf; + const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE; + size_t copied = 0; + + if (pos >= entry->size) + return 0; + + if (pos + count > entry->size) + count = entry->size - pos; + + resp_buf = kmalloc(block_size, GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + while (count > 0) { + /* SCARLETT2_USB_GET_DEVMAP reads only on block boundaries, + * so we need to read a whole block and copy the requested + * chunk to userspace. + */ + + __le32 req; + int err; + + /* offset within the block that we're reading */ + size_t offset = pos % block_size; + + /* read_size is block_size except for the last block */ + size_t block_start = pos - offset; + size_t read_size = min_t(size_t, + block_size, + entry->size - block_start); + + /* size of the chunk to copy to userspace */ + size_t copy_size = min_t(size_t, count, read_size - offset); + + /* request the block */ + req = cpu_to_le32(pos / block_size); + err = scarlett2_usb(mixer, SCARLETT2_USB_GET_DEVMAP, + &req, sizeof(req), resp_buf, read_size); + if (err < 0) { + kfree(resp_buf); + return copied ? copied : err; + } + + if (copy_to_user(buf, resp_buf + offset, copy_size)) { + kfree(resp_buf); + return -EFAULT; + } + + buf += copy_size; + pos += copy_size; + copied += copy_size; + count -= copy_size; + } + + kfree(resp_buf); + return copied; +} + +static const struct snd_info_entry_ops scarlett2_devmap_ops = { + .read = scarlett2_devmap_read, +}; + +static int scarlett2_devmap_init(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + __le16 config_len_buf[2]; + int config_len; + struct snd_info_entry *entry; + int err; + + /* If the device doesn't support the DEVMAP commands, don't + * create the /proc/asound/cardX/scarlett.json.zlib entry + */ + if (!info->has_devmap) + return 0; + + err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_DEVMAP, + NULL, 0, &config_len_buf, sizeof(config_len_buf)); + if (err < 0) + return err; + + config_len = le16_to_cpu(config_len_buf[1]); + + err = snd_card_proc_new(card, SCARLETT2_DEVMAP_FILENAME, &entry); + if (err < 0) + return err; + + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = mixer; + entry->c.ops = &scarlett2_devmap_ops; + entry->size = config_len; + entry->mode = S_IFREG | 0444; + + return 0; +} + int snd_scarlett2_init(struct usb_mixer_interface *mixer) { struct snd_usb_audio *chip = mixer->chip; @@ -9641,11 +9742,20 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer) } err = scarlett2_hwdep_init(mixer); - if (err < 0) + if (err < 0) { usb_audio_err(mixer->chip, "Error creating %s hwdep device: %d", entry->series_name, err); + return err; + } + + err = scarlett2_devmap_init(mixer); + if (err < 0) + usb_audio_err(mixer->chip, + "Error creating %s devmap entry: %d", + entry->series_name, + err); return err; } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 24c981c9b240..c49383e64678 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -324,7 +324,6 @@ YAMAHA_DEVICE(0x105a, NULL), YAMAHA_DEVICE(0x105b, NULL), YAMAHA_DEVICE(0x105c, NULL), YAMAHA_DEVICE(0x105d, NULL), -YAMAHA_DEVICE(0x1718, "P-125"), { USB_DEVICE(0x0499, 0x1503), QUIRK_DRIVER_INFO { @@ -391,6 +390,19 @@ YAMAHA_DEVICE(0x1718, "P-125"), } } }, +{ + USB_DEVICE(0x0499, 0x1718), + QUIRK_DRIVER_INFO { + /* .vendor_name = "Yamaha", */ + /* .product_name = "P-125", */ + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_STANDARD_AUDIO(1) }, + { QUIRK_DATA_STANDARD_AUDIO(2) }, + { QUIRK_DATA_MIDI_YAMAHA(3) }, + QUIRK_COMPOSITE_END + } + } +}, YAMAHA_DEVICE(0x2000, "DGP-7"), YAMAHA_DEVICE(0x2001, "DGP-5"), YAMAHA_DEVICE(0x2002, NULL), @@ -3119,6 +3131,63 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +{ + /* + * Pioneer DJ / AlphaTheta DJM-A9 + * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x003c), + QUIRK_DRIVER_INFO { + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_AUDIOFORMAT(0) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 10, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + { + QUIRK_DATA_AUDIOFORMAT(0) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 12, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_idx = 1, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + QUIRK_COMPOSITE_END + } + } +}, + /* * MacroSilicon MS2100/MS2106 based AV capture cards * diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c5fd180357d1..8bc959b60be3 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -555,6 +555,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD || @@ -566,10 +567,14 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac if (err < 0) dev_dbg(&dev->dev, "error sending boot message: %d\n", err); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err); @@ -901,6 +906,7 @@ static void mbox2_setup_48_24_magic(struct usb_device *dev) static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; u8 bootresponse[0x12]; int fwsize; @@ -936,10 +942,14 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "device initialised!\n"); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) @@ -1249,6 +1259,7 @@ static void mbox3_setup_defaults(struct usb_device *dev) static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; int descriptor_size; @@ -1262,10 +1273,14 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "MBOX3: device initialised!\n"); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "MBOX3: error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "MBOX3: error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) @@ -2115,7 +2130,7 @@ struct usb_audio_quirk_flags_table { static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { /* Device matches */ DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ - QUIRK_FLAG_GET_SAMPLE_RATE), + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ @@ -2123,10 +2138,31 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY | QUIRK_FLAG_DISABLE_AUTOSUSPEND), + DEVICE_FLG(0x046d, 0x0807, /* Logitech Webcam C500 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x0808, /* Logitech Webcam C600 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x0809, + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x0819, /* Logitech Webcam C210 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x081b, /* HD Webcam c310 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x081d, /* HD Webcam c510 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x0825, /* HD Webcam c270 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x0826, /* HD Webcam c525 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), DEVICE_FLG(0x046d, 0x084c, /* Logitech ConferenceCam Connect */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x046d, 0x08ca, /* Logitech Quickcam Fusion */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), DEVICE_FLG(0x046d, 0x0991, /* Logitech QuickCam Pro */ - QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR), + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR | + QUIRK_FLAG_MIC_RES_384), + DEVICE_FLG(0x046d, 0x09a2, /* QuickCam Communicate Deluxe/S7500 */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384), DEVICE_FLG(0x046d, 0x09a4, /* Logitech QuickCam E 3500 */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ @@ -2194,7 +2230,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */ QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ - QUIRK_FLAG_GET_SAMPLE_RATE), + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */ @@ -2232,9 +2268,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */ - QUIRK_FLAG_GET_SAMPLE_RATE), + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ - QUIRK_FLAG_GET_SAMPLE_RATE), + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */ QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b0f042c99608..158ec053dc44 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -194,6 +194,8 @@ extern bool snd_usb_skip_validation; * QUIRK_FLAG_FIXED_RATE * Do not set PCM rate (frequency) when only one rate is available * for the given endpoint. + * QUIRK_FLAG_MIC_RES_16 and QUIRK_FLAG_MIC_RES_384 + * Set the fixed resolution for Mic Capture Volume (mostly for webcams) */ #define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0) @@ -218,5 +220,7 @@ extern bool snd_usb_skip_validation; #define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19) #define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20) #define QUIRK_FLAG_FIXED_RATE (1U << 21) +#define QUIRK_FLAG_MIC_RES_16 (1U << 22) +#define QUIRK_FLAG_MIC_RES_384 (1U << 23) #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 1be0e980feb9..6bcf8b859ebb 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -89,13 +89,6 @@ static void pt_info_set(struct usb_device *dev, u8 v) v, 0, NULL, 0, 1000, GFP_NOIO); } -static void usb_stream_hwdep_vm_open(struct vm_area_struct *area) -{ - struct us122l *us122l = area->vm_private_data; - - atomic_inc(&us122l->mmap_count); -} - static vm_fault_t usb_stream_hwdep_vm_fault(struct vm_fault *vmf) { unsigned long offset; @@ -132,17 +125,9 @@ unlock: return VM_FAULT_SIGBUS; } -static void usb_stream_hwdep_vm_close(struct vm_area_struct *area) -{ - struct us122l *us122l = area->vm_private_data; - - atomic_dec(&us122l->mmap_count); -} static const struct vm_operations_struct usb_stream_hwdep_vm_ops = { - .open = usb_stream_hwdep_vm_open, .fault = usb_stream_hwdep_vm_fault, - .close = usb_stream_hwdep_vm_close, }; static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file) @@ -218,7 +203,6 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw, if (!read) vm_flags_set(area, VM_DONTEXPAND); area->vm_private_data = us122l; - atomic_inc(&us122l->mmap_count); out: mutex_unlock(&us122l->mutex); return err; @@ -606,10 +590,7 @@ static void snd_us122l_disconnect(struct usb_interface *intf) usb_put_intf(usb_ifnum_to_if(us122l->dev, 1)); usb_put_dev(us122l->dev); - while (atomic_read(&us122l->mmap_count)) - msleep(500); - - snd_card_free(card); + snd_card_free_when_closed(card); } static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h index c32ae5e981e9..8e59b78d3514 100644 --- a/sound/usb/usx2y/us122l.h +++ b/sound/usb/usx2y/us122l.h @@ -16,8 +16,6 @@ struct us122l { struct file *slave; struct list_head midi_list; - atomic_t mmap_count; - bool is_us144; }; diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 2f9cede242b3..5f81c68fd42b 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -422,7 +422,7 @@ static void snd_usx2y_disconnect(struct usb_interface *intf) } if (usx2y->us428ctls_sharedmem) wake_up(&usx2y->us428ctls_wait_queue_head); - snd_card_free(card); + snd_card_free_when_closed(card); } static int snd_usx2y_probe(struct usb_interface *intf, diff --git a/tools/testing/selftests/alsa/.gitignore b/tools/testing/selftests/alsa/.gitignore index 12dc3fcd3456..3dd8e1176b89 100644 --- a/tools/testing/selftests/alsa/.gitignore +++ b/tools/testing/selftests/alsa/.gitignore @@ -1,3 +1,5 @@ +global-timer mixer-test pcm-test test-pcmtest-driver +utimer-test |