Merge branch 'for-next' into for-linus
This commit is contained in:
commit
aa21c3d4b9
|
@ -2234,6 +2234,19 @@ use_vmalloc
|
|||
buffers. If mmap is used on such architectures, turn off this
|
||||
option, so that the DMA-coherent buffers are allocated and used
|
||||
instead.
|
||||
delayed_register
|
||||
The option is needed for devices that have multiple streams
|
||||
defined in multiple USB interfaces. The driver may invoke
|
||||
registrations multiple times (once per interface) and this may
|
||||
lead to the insufficient device enumeration.
|
||||
This option receives an array of strings, and you can pass
|
||||
ID:INTERFACE like ``0123abcd:4`` for performing the delayed
|
||||
registration to the given device. In this example, when a USB
|
||||
device 0123:abcd is probed, the driver waits the registration
|
||||
until the USB interface 4 gets probed.
|
||||
The driver prints a message like "Found post-registration device
|
||||
assignment: 1234abcd:04" for such a device, so that user can
|
||||
notice the need.
|
||||
|
||||
This module supports multiple devices, autoprobe and hotplugging.
|
||||
|
||||
|
|
|
@ -156,6 +156,18 @@ struct uac2_feature_unit_descriptor {
|
|||
__u8 bmaControls[0]; /* variable length */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.10 Effect Unit Descriptor */
|
||||
|
||||
struct uac2_effect_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__le16 wEffectType;
|
||||
__u8 bSourceID;
|
||||
__u8 bmaControls[]; /* variable length */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.9.2 Class-Specific AS Interface Descriptor */
|
||||
|
||||
struct uac2_as_header_descriptor {
|
||||
|
|
|
@ -266,6 +266,7 @@ void snd_device_disconnect(struct snd_card *card, void *device_data);
|
|||
void snd_device_disconnect_all(struct snd_card *card);
|
||||
void snd_device_free(struct snd_card *card, void *device_data);
|
||||
void snd_device_free_all(struct snd_card *card);
|
||||
int snd_device_get_state(struct snd_card *card, void *device_data);
|
||||
|
||||
/* isadma.c */
|
||||
|
||||
|
|
|
@ -1415,6 +1415,15 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
|
|||
return 1ULL << (__force int) pcm_format;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcm_for_each_format - helper to iterate for each format type
|
||||
* @f: the iterator variable in snd_pcm_format_t type
|
||||
*/
|
||||
#define pcm_for_each_format(f) \
|
||||
for ((f) = SNDRV_PCM_FORMAT_FIRST; \
|
||||
(__force int)(f) <= (__force int)SNDRV_PCM_FORMAT_LAST; \
|
||||
(f) = (__force snd_pcm_format_t)((__force int)(f) + 1))
|
||||
|
||||
/* printk helpers */
|
||||
#define pcm_err(pcm, fmt, args...) \
|
||||
dev_err((pcm)->card->dev, fmt, ##args)
|
||||
|
|
|
@ -133,6 +133,13 @@ static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val)
|
|||
return mask->bits[MASK_OFS(val)] & MASK_BIT(val);
|
||||
}
|
||||
|
||||
/* Most of drivers need only this one */
|
||||
static inline int snd_mask_test_format(const struct snd_mask *mask,
|
||||
snd_pcm_format_t format)
|
||||
{
|
||||
return snd_mask_test(mask, (__force unsigned int)format);
|
||||
}
|
||||
|
||||
static inline int snd_mask_single(const struct snd_mask *mask)
|
||||
{
|
||||
int i, c = 0;
|
||||
|
|
|
@ -237,3 +237,24 @@ void snd_device_free_all(struct snd_card *card)
|
|||
list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
|
||||
__snd_device_free(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_device_get_state - Get the current state of the given device
|
||||
* @card: the card instance
|
||||
* @device_data: the data pointer to release
|
||||
*
|
||||
* Returns the current state of the given device object. For the valid
|
||||
* device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or
|
||||
* @SNDRV_DEV_DISCONNECTED is returned.
|
||||
* Or for a non-existing device, -1 is returned as an error.
|
||||
*/
|
||||
int snd_device_get_state(struct snd_card *card, void *device_data)
|
||||
{
|
||||
struct snd_device *dev;
|
||||
|
||||
dev = look_for_dev(card, device_data);
|
||||
if (dev)
|
||||
return dev->state;
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_device_get_state);
|
||||
|
|
|
@ -604,7 +604,7 @@ int snd_info_card_free(struct snd_card *card)
|
|||
*/
|
||||
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
|
||||
{
|
||||
int c = -1;
|
||||
int c;
|
||||
|
||||
if (snd_BUG_ON(!buffer || !buffer->buffer))
|
||||
return 1;
|
||||
|
|
|
@ -884,20 +884,17 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
|
|||
sformat = snd_pcm_plug_slave_format(format, sformat_mask);
|
||||
|
||||
if ((__force int)sformat < 0 ||
|
||||
!snd_mask_test(sformat_mask, (__force int)sformat)) {
|
||||
for (sformat = (__force snd_pcm_format_t)0;
|
||||
(__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST;
|
||||
sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {
|
||||
if (snd_mask_test(sformat_mask, (__force int)sformat) &&
|
||||
!snd_mask_test_format(sformat_mask, sformat)) {
|
||||
pcm_for_each_format(sformat) {
|
||||
if (snd_mask_test_format(sformat_mask, sformat) &&
|
||||
snd_pcm_oss_format_to(sformat) >= 0)
|
||||
break;
|
||||
}
|
||||
if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) {
|
||||
pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
|
||||
err = -EINVAL;
|
||||
goto failure;
|
||||
goto format_found;
|
||||
}
|
||||
pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
|
||||
err = -EINVAL;
|
||||
goto failure;
|
||||
}
|
||||
format_found:
|
||||
err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
|
||||
if (err < 0)
|
||||
goto failure;
|
||||
|
@ -1220,8 +1217,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
|
|||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&runtime->oss.params_lock);
|
||||
ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
|
||||
frames, in_kernel);
|
||||
mutex_lock(&runtime->oss.params_lock);
|
||||
if (ret != -EPIPE && ret != -ESTRPIPE)
|
||||
break;
|
||||
/* test, if we can't store new data, because the stream */
|
||||
|
@ -1257,8 +1256,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
|
|||
ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
|
||||
if (ret < 0)
|
||||
break;
|
||||
mutex_unlock(&runtime->oss.params_lock);
|
||||
ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
|
||||
frames, in_kernel);
|
||||
mutex_lock(&runtime->oss.params_lock);
|
||||
if (ret == -EPIPE) {
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||
|
|
|
@ -196,82 +196,74 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
|
||||
snd_pcm_sframes_t frames)
|
||||
{
|
||||
struct snd_pcm_plugin *plugin, *plugin_next;
|
||||
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_frames) {
|
||||
frames = plugin->dst_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
if (frames > plugin->buf_frames)
|
||||
frames = plugin->buf_frames;
|
||||
plugin = plugin_next;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
|
||||
snd_pcm_sframes_t frames)
|
||||
{
|
||||
struct snd_pcm_plugin *plugin, *plugin_prev;
|
||||
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin && frames > 0) {
|
||||
if (frames > plugin->buf_frames)
|
||||
frames = plugin->buf_frames;
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_frames) {
|
||||
frames = plugin->src_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
|
||||
{
|
||||
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
|
||||
int stream;
|
||||
|
||||
if (snd_BUG_ON(!plug))
|
||||
return -ENXIO;
|
||||
if (drv_frames == 0)
|
||||
return 0;
|
||||
stream = snd_pcm_plug_stream(plug);
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin && drv_frames > 0) {
|
||||
if (drv_frames > plugin->buf_frames)
|
||||
drv_frames = plugin->buf_frames;
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_frames)
|
||||
drv_frames = plugin->src_frames(plugin, drv_frames);
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && drv_frames > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_frames)
|
||||
drv_frames = plugin->dst_frames(plugin, drv_frames);
|
||||
if (drv_frames > plugin->buf_frames)
|
||||
drv_frames = plugin->buf_frames;
|
||||
plugin = plugin_next;
|
||||
}
|
||||
} else
|
||||
switch (snd_pcm_plug_stream(plug)) {
|
||||
case SNDRV_PCM_STREAM_PLAYBACK:
|
||||
return calc_src_frames(plug, drv_frames);
|
||||
case SNDRV_PCM_STREAM_CAPTURE:
|
||||
return calc_dst_frames(plug, drv_frames);
|
||||
default:
|
||||
snd_BUG();
|
||||
return drv_frames;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
|
||||
{
|
||||
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
|
||||
snd_pcm_sframes_t frames;
|
||||
int stream;
|
||||
|
||||
if (snd_BUG_ON(!plug))
|
||||
return -ENXIO;
|
||||
if (clt_frames == 0)
|
||||
return 0;
|
||||
frames = clt_frames;
|
||||
stream = snd_pcm_plug_stream(plug);
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_frames) {
|
||||
frames = plugin->dst_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
if (frames > plugin->buf_frames)
|
||||
frames = plugin->buf_frames;
|
||||
plugin = plugin_next;
|
||||
}
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin) {
|
||||
if (frames > plugin->buf_frames)
|
||||
frames = plugin->buf_frames;
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_frames) {
|
||||
frames = plugin->src_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
} else
|
||||
switch (snd_pcm_plug_stream(plug)) {
|
||||
case SNDRV_PCM_STREAM_PLAYBACK:
|
||||
return calc_dst_frames(plug, clt_frames);
|
||||
case SNDRV_PCM_STREAM_CAPTURE:
|
||||
return calc_src_frames(plug, clt_frames);
|
||||
default:
|
||||
snd_BUG();
|
||||
return frames;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_plug_formats(const struct snd_mask *mask,
|
||||
|
|
|
@ -47,7 +47,7 @@ struct rate_priv {
|
|||
unsigned int pos;
|
||||
rate_f func;
|
||||
snd_pcm_sframes_t old_src_frames, old_dst_frames;
|
||||
struct rate_channel channels[0];
|
||||
struct rate_channel channels[];
|
||||
};
|
||||
|
||||
static void rate_init(struct snd_pcm_plugin *plugin)
|
||||
|
|
|
@ -1019,7 +1019,7 @@ static ssize_t show_pcm_class(struct device *dev,
|
|||
str = "none";
|
||||
else
|
||||
str = strs[pcm->dev_class];
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str);
|
||||
return sprintf(buf, "%s\n", str);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL);
|
||||
|
|
|
@ -426,7 +426,7 @@ int snd_dmaengine_pcm_refine_runtime_hwparams(
|
|||
* default assumption is that it supports 1, 2 and 4 bytes
|
||||
* widths.
|
||||
*/
|
||||
for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
pcm_for_each_format(i) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
|
||||
/*
|
||||
|
|
|
@ -42,6 +42,11 @@ struct pcm_format_data {
|
|||
/* we do lots of calculations on snd_pcm_format_t; shut up sparse */
|
||||
#define INT __force int
|
||||
|
||||
static bool valid_format(snd_pcm_format_t format)
|
||||
{
|
||||
return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST;
|
||||
}
|
||||
|
||||
static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
|
||||
[SNDRV_PCM_FORMAT_S8] = {
|
||||
.width = 8, .phys = 8, .le = -1, .signd = 1,
|
||||
|
@ -259,7 +264,7 @@ static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] =
|
|||
int snd_pcm_format_signed(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].signd) < 0)
|
||||
return -EINVAL;
|
||||
|
@ -307,7 +312,7 @@ EXPORT_SYMBOL(snd_pcm_format_linear);
|
|||
int snd_pcm_format_little_endian(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].le) < 0)
|
||||
return -EINVAL;
|
||||
|
@ -343,7 +348,7 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian);
|
|||
int snd_pcm_format_width(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].width) == 0)
|
||||
return -EINVAL;
|
||||
|
@ -361,7 +366,7 @@ EXPORT_SYMBOL(snd_pcm_format_width);
|
|||
int snd_pcm_format_physical_width(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].phys) == 0)
|
||||
return -EINVAL;
|
||||
|
@ -394,7 +399,7 @@ EXPORT_SYMBOL(snd_pcm_format_size);
|
|||
*/
|
||||
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
|
||||
{
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return NULL;
|
||||
if (! pcm_formats[(INT)format].phys)
|
||||
return NULL;
|
||||
|
@ -418,7 +423,7 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
|
|||
unsigned char *dst;
|
||||
const unsigned char *pat;
|
||||
|
||||
if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if (samples == 0)
|
||||
return 0;
|
||||
|
|
|
@ -228,6 +228,9 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* macro for simplified cast */
|
||||
#define PARAM_MASK_BIT(b) (1U << (__force int)(b))
|
||||
|
||||
static bool hw_support_mmap(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
|
||||
|
@ -257,7 +260,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
|
||||
/* This parameter is not requested to change by a caller. */
|
||||
if (!(params->rmask & (1 << k)))
|
||||
if (!(params->rmask & PARAM_MASK_BIT(k)))
|
||||
continue;
|
||||
|
||||
if (trace_hw_mask_param_enabled())
|
||||
|
@ -271,7 +274,7 @@ static int constrain_mask_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Set corresponding flag so that the caller gets it. */
|
||||
trace_hw_mask_param(substream, k, 0, &old_mask, m);
|
||||
params->cmask |= 1 << k;
|
||||
params->cmask |= PARAM_MASK_BIT(k);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -293,7 +296,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
|
||||
/* This parameter is not requested to change by a caller. */
|
||||
if (!(params->rmask & (1 << k)))
|
||||
if (!(params->rmask & PARAM_MASK_BIT(k)))
|
||||
continue;
|
||||
|
||||
if (trace_hw_interval_param_enabled())
|
||||
|
@ -307,7 +310,7 @@ static int constrain_interval_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Set corresponding flag so that the caller gets it. */
|
||||
trace_hw_interval_param(substream, k, 0, &old_interval, i);
|
||||
params->cmask |= 1 << k;
|
||||
params->cmask |= PARAM_MASK_BIT(k);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -349,7 +352,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
|
|||
* have 0 so that the parameters are never changed anymore.
|
||||
*/
|
||||
for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
|
||||
vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
|
||||
vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0;
|
||||
|
||||
/* Due to the above design, actual sequence number starts at 2. */
|
||||
stamp = 2;
|
||||
|
@ -417,7 +420,7 @@ retry:
|
|||
hw_param_interval(params, r->var));
|
||||
}
|
||||
|
||||
params->cmask |= (1 << r->var);
|
||||
params->cmask |= PARAM_MASK_BIT(r->var);
|
||||
vstamps[r->var] = stamp;
|
||||
again = true;
|
||||
}
|
||||
|
@ -486,9 +489,9 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
|
|||
|
||||
params->info = 0;
|
||||
params->fifo_size = 0;
|
||||
if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
|
||||
if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
|
||||
params->msbits = 0;
|
||||
if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
|
||||
if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) {
|
||||
params->rate_num = 0;
|
||||
params->rate_den = 0;
|
||||
}
|
||||
|
@ -2293,21 +2296,21 @@ static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,
|
|||
static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
unsigned int k;
|
||||
snd_pcm_format_t k;
|
||||
const struct snd_interval *i =
|
||||
hw_param_interval_c(params, rule->deps[0]);
|
||||
struct snd_mask m;
|
||||
struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_any(&m);
|
||||
for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
|
||||
pcm_for_each_format(k) {
|
||||
int bits;
|
||||
if (! snd_mask_test(mask, k))
|
||||
if (!snd_mask_test_format(mask, k))
|
||||
continue;
|
||||
bits = snd_pcm_format_physical_width(k);
|
||||
if (bits <= 0)
|
||||
continue; /* ignore invalid formats */
|
||||
if ((unsigned)bits < i->min || (unsigned)bits > i->max)
|
||||
snd_mask_reset(&m, k);
|
||||
snd_mask_reset(&m, (__force unsigned)k);
|
||||
}
|
||||
return snd_mask_refine(mask, &m);
|
||||
}
|
||||
|
@ -2316,14 +2319,15 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
|
|||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_interval t;
|
||||
unsigned int k;
|
||||
snd_pcm_format_t k;
|
||||
|
||||
t.min = UINT_MAX;
|
||||
t.max = 0;
|
||||
t.openmin = 0;
|
||||
t.openmax = 0;
|
||||
for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
|
||||
pcm_for_each_format(k) {
|
||||
int bits;
|
||||
if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
|
||||
if (!snd_mask_test_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
|
||||
continue;
|
||||
bits = snd_pcm_format_physical_width(k);
|
||||
if (bits <= 0)
|
||||
|
@ -2505,16 +2509,16 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
|
|||
unsigned int mask = 0;
|
||||
|
||||
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
|
||||
mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
|
||||
mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
|
||||
mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
|
||||
if (hw_support_mmap(substream)) {
|
||||
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
|
||||
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
|
||||
mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
|
||||
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
|
||||
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
|
||||
mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
||||
if (hw->info & SNDRV_PCM_INFO_COMPLEX)
|
||||
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
|
||||
mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX);
|
||||
}
|
||||
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
|
||||
if (err < 0)
|
||||
|
@ -2524,7 +2528,8 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
|
||||
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
|
||||
PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ struct loopback_cable {
|
|||
struct loopback_setup {
|
||||
unsigned int notify: 1;
|
||||
unsigned int rate_shift;
|
||||
unsigned int format;
|
||||
snd_pcm_format_t format;
|
||||
unsigned int rate;
|
||||
unsigned int channels;
|
||||
struct snd_ctl_elem_id active_id;
|
||||
|
@ -1432,7 +1432,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol,
|
|||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
|
||||
uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST;
|
||||
uinfo->value.integer.step = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1443,7 +1443,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol,
|
|||
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] =
|
||||
loopback->setup[kcontrol->id.subdevice]
|
||||
(__force int)loopback->setup[kcontrol->id.subdevice]
|
||||
[kcontrol->id.device].format;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -901,10 +901,10 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
|
|||
static void print_formats(struct snd_dummy *dummy,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
int i;
|
||||
snd_pcm_format_t i;
|
||||
|
||||
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
if (dummy->pcm_hw.formats & (1ULL << i))
|
||||
pcm_for_each_format(i) {
|
||||
if (dummy->pcm_hw.formats & pcm_format_to_bits(i))
|
||||
snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -509,7 +509,7 @@ MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
|
|||
static struct fw_driver bebob_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-bebob",
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = bebob_probe,
|
||||
|
|
|
@ -192,7 +192,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
|
|||
static struct fw_driver dg00x_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-firewire-digi00x",
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = snd_dg00x_probe,
|
||||
|
|
|
@ -224,7 +224,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
|
|||
static struct fw_driver ff_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-fireface",
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = snd_ff_probe,
|
||||
|
|
|
@ -362,7 +362,7 @@ MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
|
|||
static struct fw_driver efw_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-fireworks",
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = efw_probe,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
|
||||
long count, loff_t *offset)
|
||||
__releases(&tscm->lock)
|
||||
{
|
||||
struct snd_firewire_event_lock_status event = {
|
||||
.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
|
||||
|
@ -36,6 +37,7 @@ static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
|
|||
|
||||
static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
|
||||
long remained, loff_t *offset)
|
||||
__releases(&tscm->lock)
|
||||
{
|
||||
char __user *pos = buf;
|
||||
unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
|
||||
|
|
|
@ -224,7 +224,7 @@ MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
|
|||
static struct fw_driver tscm_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-firewire-tascam",
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = snd_tscm_probe,
|
||||
|
|
|
@ -204,7 +204,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
|
|||
*/
|
||||
int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
|
||||
{
|
||||
return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
|
||||
return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
|
||||
codec->vendor_id, codec->revision_id, codec->type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
|
||||
|
|
|
@ -435,7 +435,7 @@ enum {
|
|||
#define LOOP_WRITE(rec, offset, _buf, count, mode) \
|
||||
do { \
|
||||
struct snd_emu8000 *emu = (rec)->emu; \
|
||||
unsigned short *buf = (unsigned short *)(_buf); \
|
||||
unsigned short *buf = (__force unsigned short *)(_buf); \
|
||||
snd_emu8000_write_wait(emu, 1); \
|
||||
EMU8000_SMALW_WRITE(emu, offset); \
|
||||
while (count > 0) { \
|
||||
|
@ -492,7 +492,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
|
|||
#define LOOP_WRITE(rec, pos, _buf, count, mode) \
|
||||
do { \
|
||||
struct snd_emu8000 *emu = rec->emu; \
|
||||
unsigned short *buf = (unsigned short *)(_buf); \
|
||||
unsigned short *buf = (__force unsigned short *)(_buf); \
|
||||
snd_emu8000_write_wait(emu, 1); \
|
||||
EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \
|
||||
if (rec->voices > 1) \
|
||||
|
|
|
@ -1070,7 +1070,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_ali *codec = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_substream *s;
|
||||
unsigned int what, whati, capture_flag;
|
||||
unsigned int what, whati;
|
||||
struct snd_ali_voice *pvoice, *evoice;
|
||||
unsigned int val;
|
||||
int do_start;
|
||||
|
@ -1088,7 +1088,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
what = whati = capture_flag = 0;
|
||||
what = whati = 0;
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) {
|
||||
pvoice = s->runtime->private_data;
|
||||
|
@ -1110,8 +1110,6 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
|
|||
evoice->running = 0;
|
||||
}
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
if (pvoice->mode)
|
||||
capture_flag = 1;
|
||||
}
|
||||
}
|
||||
spin_lock(&codec->reg_lock);
|
||||
|
|
|
@ -1789,6 +1789,7 @@ int snd_emu10k1_create(struct snd_card *card,
|
|||
int idx, err;
|
||||
int is_audigy;
|
||||
size_t page_table_size;
|
||||
__le32 *pgtbl;
|
||||
unsigned int silent_page;
|
||||
const struct snd_emu_chip_details *c;
|
||||
static const struct snd_device_ops ops = {
|
||||
|
@ -2009,8 +2010,9 @@ int snd_emu10k1_create(struct snd_card *card,
|
|||
/* Clear silent pages and set up pointers */
|
||||
memset(emu->silent_page.area, 0, emu->silent_page.bytes);
|
||||
silent_page = emu->silent_page.addr << emu->address_mode;
|
||||
pgtbl = (__le32 *)emu->ptb_pages.area;
|
||||
for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
|
||||
((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
|
||||
pgtbl[idx] = cpu_to_le32(silent_page | idx);
|
||||
|
||||
/* set up voice indices */
|
||||
for (idx = 0; idx < NUM_G; idx++) {
|
||||
|
|
|
@ -184,6 +184,7 @@ comment "Set to Y if you want auto-loading the codec driver"
|
|||
config SND_HDA_CODEC_CA0132_DSP
|
||||
bool "Support new DSP code for CA0132 codec"
|
||||
depends on SND_HDA_CODEC_CA0132
|
||||
default y
|
||||
select SND_HDA_DSP_LOADER
|
||||
select FW_LOADER
|
||||
help
|
||||
|
|
|
@ -88,7 +88,7 @@ struct hda_conn_list {
|
|||
struct list_head list;
|
||||
int len;
|
||||
hda_nid_t nid;
|
||||
hda_nid_t conns[0];
|
||||
hda_nid_t conns[];
|
||||
};
|
||||
|
||||
/* look up the cached results */
|
||||
|
|
|
@ -373,7 +373,7 @@ static int azx_get_sync_time(ktime_t *device,
|
|||
u32 wallclk_ctr, wallclk_cycles;
|
||||
bool direction;
|
||||
u32 dma_select;
|
||||
u32 timeout = 200;
|
||||
u32 timeout;
|
||||
u32 retry_count = 0;
|
||||
|
||||
runtime = substream->runtime;
|
||||
|
|
|
@ -2699,7 +2699,7 @@ struct dsp_image_seg {
|
|||
u32 magic;
|
||||
u32 chip_addr;
|
||||
u32 count;
|
||||
u32 data[0];
|
||||
u32 data[];
|
||||
};
|
||||
|
||||
static const u32 g_magic_value = 0x4c46584d;
|
||||
|
|
|
@ -154,7 +154,6 @@ struct hdmi_spec {
|
|||
struct hda_multi_out multiout;
|
||||
struct hda_pcm_stream pcm_playback;
|
||||
|
||||
bool use_jack_detect; /* jack detection enabled */
|
||||
bool use_acomp_notifier; /* use eld_notify callback for hotplug */
|
||||
bool acomp_registered; /* audio component registered in this driver */
|
||||
struct drm_audio_component_audio_ops drm_audio_ops;
|
||||
|
@ -753,7 +752,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|||
* Unsolicited events
|
||||
*/
|
||||
|
||||
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
|
||||
static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
|
||||
|
||||
static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dev_id)
|
||||
|
@ -764,8 +763,7 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
|
|||
if (pin_idx < 0)
|
||||
return;
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
|
||||
snd_hda_jack_report_sync(codec);
|
||||
hdmi_present_sense(get_pin(spec, pin_idx), 1);
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
}
|
||||
|
||||
|
@ -779,21 +777,9 @@ static void jack_callback(struct hda_codec *codec,
|
|||
check_presence_and_report(codec, jack->nid, jack->dev_id);
|
||||
}
|
||||
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res,
|
||||
struct hda_jack_tbl *jack)
|
||||
{
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
struct hda_jack_tbl *jack;
|
||||
|
||||
if (codec->dp_mst) {
|
||||
int dev_entry =
|
||||
(res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
|
||||
|
||||
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
|
||||
} else {
|
||||
jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
|
||||
}
|
||||
if (!jack)
|
||||
return;
|
||||
jack->jack_dirty = 1;
|
||||
|
||||
codec_dbg(codec,
|
||||
|
@ -853,7 +839,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
|||
}
|
||||
|
||||
if (subtag == 0)
|
||||
hdmi_intrinsic_event(codec, res);
|
||||
hdmi_intrinsic_event(codec, res, jack);
|
||||
else
|
||||
hdmi_non_intrinsic_event(codec, res);
|
||||
}
|
||||
|
@ -1480,21 +1466,60 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
|
|||
per_pin->channels = 0;
|
||||
}
|
||||
|
||||
static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (per_pin->pcm_idx >= 0)
|
||||
return spec->pcm_rec[per_pin->pcm_idx].jack;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* update per_pin ELD from the given new ELD;
|
||||
* setup info frame and notification accordingly
|
||||
* also notify ELD kctl and report jack status changes
|
||||
*/
|
||||
static bool update_eld(struct hda_codec *codec,
|
||||
static void update_eld(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
struct hdmi_eld *eld)
|
||||
struct hdmi_eld *eld,
|
||||
int repoll)
|
||||
{
|
||||
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct snd_jack *pcm_jack;
|
||||
bool old_eld_valid = pin_eld->eld_valid;
|
||||
bool eld_changed;
|
||||
int pcm_idx;
|
||||
|
||||
if (eld->eld_valid) {
|
||||
if (eld->eld_size <= 0 ||
|
||||
snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
|
||||
eld->eld_size) < 0) {
|
||||
eld->eld_valid = false;
|
||||
if (repoll) {
|
||||
schedule_delayed_work(&per_pin->work,
|
||||
msecs_to_jiffies(300));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!eld->eld_valid || eld->eld_size <= 0) {
|
||||
eld->eld_valid = false;
|
||||
eld->eld_size = 0;
|
||||
}
|
||||
|
||||
/* for monitor disconnection, save pcm_idx firstly */
|
||||
pcm_idx = per_pin->pcm_idx;
|
||||
|
||||
/*
|
||||
* pcm_idx >=0 before update_eld() means it is in monitor
|
||||
* disconnected event. Jack must be fetched before update_eld().
|
||||
*/
|
||||
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
|
||||
if (spec->dyn_pcm_assign) {
|
||||
if (eld->eld_valid) {
|
||||
hdmi_attach_hda_pcm(spec, per_pin);
|
||||
|
@ -1509,6 +1534,8 @@ static bool update_eld(struct hda_codec *codec,
|
|||
*/
|
||||
if (pcm_idx == -1)
|
||||
pcm_idx = per_pin->pcm_idx;
|
||||
if (!pcm_jack)
|
||||
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
|
||||
if (eld->eld_valid)
|
||||
snd_hdmi_show_eld(codec, &eld->info);
|
||||
|
@ -1547,42 +1574,17 @@ static bool update_eld(struct hda_codec *codec,
|
|||
SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
|
||||
return eld_changed;
|
||||
|
||||
if (eld_changed && pcm_jack)
|
||||
snd_jack_report(pcm_jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
SND_JACK_AVOUT : 0);
|
||||
}
|
||||
|
||||
static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct snd_jack *jack = NULL;
|
||||
struct hda_jack_tbl *jack_tbl;
|
||||
|
||||
/* if !dyn_pcm_assign, get jack from hda_jack_tbl
|
||||
* in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
|
||||
* NULL even after snd_hda_jack_tbl_clear() is called to
|
||||
* free snd_jack. This may cause access invalid memory
|
||||
* when calling snd_jack_report
|
||||
*/
|
||||
if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) {
|
||||
jack = spec->pcm_rec[per_pin->pcm_idx].jack;
|
||||
} else if (!spec->dyn_pcm_assign) {
|
||||
/*
|
||||
* jack tbl doesn't support DP MST
|
||||
* DP MST will use dyn_pcm_assign,
|
||||
* so DP MST will never come here
|
||||
*/
|
||||
jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
|
||||
per_pin->dev_id);
|
||||
if (jack_tbl)
|
||||
jack = jack_tbl->jack;
|
||||
}
|
||||
return jack;
|
||||
}
|
||||
/* update ELD and jack state via HD-audio verbs */
|
||||
static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
||||
static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
||||
int repoll)
|
||||
{
|
||||
struct hda_jack_tbl *jack;
|
||||
struct hda_codec *codec = per_pin->codec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->temp_eld;
|
||||
|
@ -1597,9 +1599,11 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
|||
* the unsolicited response to avoid custom WARs.
|
||||
*/
|
||||
int present;
|
||||
bool ret;
|
||||
bool do_repoll = false;
|
||||
struct snd_jack *pcm_jack = NULL;
|
||||
int ret;
|
||||
|
||||
ret = snd_hda_power_up_pm(codec);
|
||||
if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec)))
|
||||
goto out;
|
||||
|
||||
present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
|
||||
|
||||
|
@ -1618,62 +1622,12 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
|||
if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
|
||||
eld->eld_buffer, &eld->eld_size) < 0)
|
||||
eld->eld_valid = false;
|
||||
else {
|
||||
if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
|
||||
eld->eld_size) < 0)
|
||||
eld->eld_valid = false;
|
||||
}
|
||||
if (!eld->eld_valid && repoll)
|
||||
do_repoll = true;
|
||||
}
|
||||
|
||||
if (do_repoll) {
|
||||
schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
|
||||
} else {
|
||||
/*
|
||||
* pcm_idx >=0 before update_eld() means it is in monitor
|
||||
* disconnected event. Jack must be fetched before
|
||||
* update_eld().
|
||||
*/
|
||||
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
update_eld(codec, per_pin, eld);
|
||||
if (!pcm_jack)
|
||||
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
}
|
||||
|
||||
ret = !repoll || !eld->monitor_present || eld->eld_valid;
|
||||
|
||||
jack = snd_hda_jack_tbl_get_mst(codec, pin_nid, per_pin->dev_id);
|
||||
if (jack) {
|
||||
jack->block_report = !ret;
|
||||
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
|
||||
AC_PINSENSE_PRESENCE : 0;
|
||||
|
||||
if (spec->dyn_pcm_assign && pcm_jack && !do_repoll) {
|
||||
int state = 0;
|
||||
|
||||
if (jack->pin_sense & AC_PINSENSE_PRESENCE)
|
||||
state = SND_JACK_AVOUT;
|
||||
snd_jack_report(pcm_jack, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_hda_jack_pin_sense() call at the beginning of this
|
||||
* function, updates jack->pins_sense and clears
|
||||
* jack->jack_dirty, therefore snd_hda_jack_report_sync() will
|
||||
* not override the jack->pin_sense.
|
||||
*
|
||||
* snd_hda_jack_report_sync() is superfluous for dyn_pcm_assign
|
||||
* case. The jack->pin_sense update was already performed, and
|
||||
* hda_jack->jack is NULL for dyn_pcm_assign.
|
||||
*
|
||||
* Don't call snd_hda_jack_report_sync() for
|
||||
* dyn_pcm_assign.
|
||||
*/
|
||||
ret = ret && !spec->dyn_pcm_assign;
|
||||
}
|
||||
update_eld(codec, per_pin, eld, repoll);
|
||||
mutex_unlock(&per_pin->lock);
|
||||
return ret;
|
||||
out:
|
||||
snd_hda_power_down_pm(codec);
|
||||
}
|
||||
|
||||
/* update ELD and jack state via audio component */
|
||||
|
@ -1682,64 +1636,25 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
|||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->temp_eld;
|
||||
struct snd_jack *jack = NULL;
|
||||
bool changed;
|
||||
int size;
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
eld->monitor_present = false;
|
||||
size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
|
||||
eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
|
||||
per_pin->dev_id, &eld->monitor_present,
|
||||
eld->eld_buffer, ELD_MAX_SIZE);
|
||||
if (size > 0) {
|
||||
size = min(size, ELD_MAX_SIZE);
|
||||
if (snd_hdmi_parse_eld(codec, &eld->info,
|
||||
eld->eld_buffer, size) < 0)
|
||||
size = -EINVAL;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
eld->eld_valid = true;
|
||||
eld->eld_size = size;
|
||||
} else {
|
||||
eld->eld_valid = false;
|
||||
eld->eld_size = 0;
|
||||
}
|
||||
|
||||
/* pcm_idx >=0 before update_eld() means it is in monitor
|
||||
* disconnected event. Jack must be fetched before update_eld()
|
||||
*/
|
||||
jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
changed = update_eld(codec, per_pin, eld);
|
||||
if (jack == NULL)
|
||||
jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||
if (changed && jack)
|
||||
snd_jack_report(jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
SND_JACK_AVOUT : 0);
|
||||
eld->eld_valid = (eld->eld_size > 0);
|
||||
update_eld(codec, per_pin, eld, 0);
|
||||
mutex_unlock(&per_pin->lock);
|
||||
}
|
||||
|
||||
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||
static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||
{
|
||||
struct hda_codec *codec = per_pin->codec;
|
||||
int ret;
|
||||
|
||||
/* no temporary power up/down needed for component notifier */
|
||||
if (!codec_has_acomp(codec)) {
|
||||
ret = snd_hda_power_up_pm(codec);
|
||||
if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec))) {
|
||||
snd_hda_power_down_pm(codec);
|
||||
return false;
|
||||
}
|
||||
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
snd_hda_power_down_pm(codec);
|
||||
} else {
|
||||
if (!codec_has_acomp(codec))
|
||||
hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
else
|
||||
sync_eld_via_acomp(codec, per_pin);
|
||||
ret = false; /* don't call snd_hda_jack_report_sync() */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hdmi_repoll_eld(struct work_struct *work)
|
||||
|
@ -1759,8 +1674,7 @@ static void hdmi_repoll_eld(struct work_struct *work)
|
|||
per_pin->repoll_count = 0;
|
||||
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
if (hdmi_present_sense(per_pin, per_pin->repoll_count))
|
||||
snd_hda_jack_report_sync(per_pin->codec);
|
||||
hdmi_present_sense(per_pin, per_pin->repoll_count);
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
}
|
||||
|
||||
|
@ -2206,15 +2120,23 @@ static void free_hdmi_jack_priv(struct snd_jack *jack)
|
|||
pcm->jack = NULL;
|
||||
}
|
||||
|
||||
static int add_hdmi_jack_kctl(struct hda_codec *codec,
|
||||
struct hdmi_spec *spec,
|
||||
int pcm_idx,
|
||||
const char *name)
|
||||
static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
|
||||
{
|
||||
char hdmi_str[32] = "HDMI/DP";
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pcm_idx);
|
||||
struct snd_jack *jack;
|
||||
int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
|
||||
int err;
|
||||
|
||||
err = snd_jack_new(codec->card, name, SND_JACK_AVOUT, &jack,
|
||||
if (pcmdev > 0)
|
||||
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
|
||||
if (!spec->dyn_pcm_assign &&
|
||||
!is_jack_detectable(codec, per_pin->pin_nid))
|
||||
strncat(hdmi_str, " Phantom",
|
||||
sizeof(hdmi_str) - strlen(hdmi_str) - 1);
|
||||
|
||||
err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack,
|
||||
true, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -2225,48 +2147,6 @@ static int add_hdmi_jack_kctl(struct hda_codec *codec,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
|
||||
{
|
||||
char hdmi_str[32] = "HDMI/DP";
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
struct hda_jack_tbl *jack;
|
||||
int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
|
||||
bool phantom_jack;
|
||||
int ret;
|
||||
|
||||
if (pcmdev > 0)
|
||||
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
|
||||
|
||||
if (spec->dyn_pcm_assign)
|
||||
return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
|
||||
|
||||
/* for !dyn_pcm_assign, we still use hda_jack for compatibility */
|
||||
/* if !dyn_pcm_assign, it must be non-MST mode.
|
||||
* This means pcms and pins are statically mapped.
|
||||
* And pcm_idx is pin_idx.
|
||||
*/
|
||||
per_pin = get_pin(spec, pcm_idx);
|
||||
phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
|
||||
if (phantom_jack)
|
||||
strncat(hdmi_str, " Phantom",
|
||||
sizeof(hdmi_str) - strlen(hdmi_str) - 1);
|
||||
ret = snd_hda_jack_add_kctl_mst(codec, per_pin->pin_nid,
|
||||
per_pin->dev_id, hdmi_str, phantom_jack,
|
||||
0, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
|
||||
per_pin->dev_id);
|
||||
if (jack == NULL)
|
||||
return 0;
|
||||
/* assign jack->jack to pcm_rec[].jack to
|
||||
* align with dyn_pcm_assign mode
|
||||
*/
|
||||
spec->pcm_rec[pcm_idx].jack = jack->jack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_hdmi_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
@ -2355,7 +2235,6 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|||
int pin_idx;
|
||||
|
||||
mutex_lock(&spec->bind_lock);
|
||||
spec->use_jack_detect = !codec->jackpoll_interval;
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
|
@ -2365,12 +2244,8 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|||
hdmi_init_pin(codec, pin_nid);
|
||||
if (codec_has_acomp(codec))
|
||||
continue;
|
||||
if (spec->use_jack_detect)
|
||||
snd_hda_jack_detect_enable(codec, pin_nid, dev_id);
|
||||
else
|
||||
snd_hda_jack_detect_enable_callback_mst(codec, pin_nid,
|
||||
dev_id,
|
||||
jack_callback);
|
||||
snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id,
|
||||
jack_callback);
|
||||
}
|
||||
mutex_unlock(&spec->bind_lock);
|
||||
return 0;
|
||||
|
@ -2532,12 +2407,6 @@ static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
|
|||
unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE, val);
|
||||
} else {
|
||||
/* if no jack entry was defined beforehand, create a new one
|
||||
* at need (i.e. only when notifier is cleared)
|
||||
*/
|
||||
if (!use_acomp)
|
||||
snd_hda_jack_detect_enable(codec, nid, dev_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2553,13 +2422,11 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
|
|||
spec->use_acomp_notifier = use_acomp;
|
||||
spec->codec->relaxed_resume = use_acomp;
|
||||
/* reprogram each jack detection logic depending on the notifier */
|
||||
if (spec->use_jack_detect) {
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
reprogram_jack_detect(spec->codec,
|
||||
get_pin(spec, i)->pin_nid,
|
||||
get_pin(spec, i)->dev_id,
|
||||
use_acomp);
|
||||
}
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
reprogram_jack_detect(spec->codec,
|
||||
get_pin(spec, i)->pin_nid,
|
||||
get_pin(spec, i)->dev_id,
|
||||
use_acomp);
|
||||
mutex_unlock(&spec->bind_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#if K1212_DEBUG_LEVEL > 0
|
||||
#define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args)
|
||||
#else
|
||||
#define K1212_DEBUG_PRINTK(fmt,...)
|
||||
#define K1212_DEBUG_PRINTK(fmt,...) do { } while (0)
|
||||
#endif
|
||||
#if K1212_DEBUG_LEVEL > 1
|
||||
#define K1212_DEBUG_PRINTK_VERBOSE(fmt,args...) printk(KERN_DEBUG fmt,##args)
|
||||
|
|
|
@ -3353,7 +3353,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
int err = -EINVAL;
|
||||
int err;
|
||||
|
||||
err = hdsp_request_fw_loader(hdsp);
|
||||
if (err < 0) {
|
||||
snd_iprintf(buffer,
|
||||
|
|
|
@ -414,6 +414,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
{
|
||||
unsigned int i, idx, ofs, rest;
|
||||
struct via82xx *chip = snd_pcm_substream_chip(substream);
|
||||
__le32 *pgtbl;
|
||||
|
||||
if (dev->table.area == NULL) {
|
||||
/* the start of each lists must be aligned to 8 bytes,
|
||||
|
@ -435,6 +436,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
/* fill the entries */
|
||||
idx = 0;
|
||||
ofs = 0;
|
||||
pgtbl = (__le32 *)dev->table.area;
|
||||
for (i = 0; i < periods; i++) {
|
||||
rest = fragsize;
|
||||
/* fill descriptors for a period.
|
||||
|
@ -451,7 +453,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
return -EINVAL;
|
||||
}
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
|
||||
((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
|
||||
pgtbl[idx << 1] = cpu_to_le32(addr);
|
||||
r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest);
|
||||
rest -= r;
|
||||
if (! rest) {
|
||||
|
@ -466,7 +468,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
"tbl %d: at %d size %d (rest %d)\n",
|
||||
idx, ofs, r, rest);
|
||||
*/
|
||||
((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
|
||||
pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag);
|
||||
dev->idx_table[idx].offset = ofs;
|
||||
dev->idx_table[idx].size = r;
|
||||
ofs += r;
|
||||
|
|
|
@ -267,6 +267,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
{
|
||||
unsigned int i, idx, ofs, rest;
|
||||
struct via82xx_modem *chip = snd_pcm_substream_chip(substream);
|
||||
__le32 *pgtbl;
|
||||
|
||||
if (dev->table.area == NULL) {
|
||||
/* the start of each lists must be aligned to 8 bytes,
|
||||
|
@ -288,6 +289,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
/* fill the entries */
|
||||
idx = 0;
|
||||
ofs = 0;
|
||||
pgtbl = (__le32 *)dev->table.area;
|
||||
for (i = 0; i < periods; i++) {
|
||||
rest = fragsize;
|
||||
/* fill descriptors for a period.
|
||||
|
@ -304,7 +306,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
return -EINVAL;
|
||||
}
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
|
||||
((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
|
||||
pgtbl[idx << 1] = cpu_to_le32(addr);
|
||||
r = PAGE_SIZE - (ofs % PAGE_SIZE);
|
||||
if (rest < r)
|
||||
r = rest;
|
||||
|
@ -321,7 +323,7 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre
|
|||
"tbl %d: at %d size %d (rest %d)\n",
|
||||
idx, ofs, r, rest);
|
||||
*/
|
||||
((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
|
||||
pgtbl[(idx<<1) + 1] = cpu_to_le32(r | flag);
|
||||
dev->idx_table[idx].offset = ofs;
|
||||
dev->idx_table[idx].size = r;
|
||||
ofs += r;
|
||||
|
|
|
@ -40,6 +40,7 @@ static int keywest_probe(struct i2c_client *client,
|
|||
static int keywest_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
struct i2c_client *client;
|
||||
|
||||
if (! keywest_ctx)
|
||||
return -EINVAL;
|
||||
|
@ -50,9 +51,11 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
|
|||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(info.type, "keywest", I2C_NAME_SIZE);
|
||||
info.addr = keywest_ctx->addr;
|
||||
keywest_ctx->client = i2c_new_device(adapter, &info);
|
||||
if (!keywest_ctx->client)
|
||||
return -ENODEV;
|
||||
client = i2c_new_client_device(adapter, &info);
|
||||
if (IS_ERR(client))
|
||||
return PTR_ERR(client);
|
||||
keywest_ctx->client = client;
|
||||
|
||||
/*
|
||||
* We know the driver is already loaded, so the device should be
|
||||
* already bound. If not it means binding failed, and then there
|
||||
|
|
|
@ -13,6 +13,7 @@ snd-usb-audio-objs := card.o \
|
|||
mixer_scarlett.o \
|
||||
mixer_scarlett_gen2.o \
|
||||
mixer_us16x08.o \
|
||||
mixer_s1810c.o \
|
||||
pcm.o \
|
||||
power.o \
|
||||
proc.o \
|
||||
|
|
|
@ -72,6 +72,7 @@ static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
|
|||
static bool ignore_ctl_error;
|
||||
static bool autoclock = true;
|
||||
static char *quirk_alias[SNDRV_CARDS];
|
||||
static char *delayed_register[SNDRV_CARDS];
|
||||
|
||||
bool snd_usb_use_vmalloc = true;
|
||||
bool snd_usb_skip_validation;
|
||||
|
@ -95,6 +96,8 @@ module_param(autoclock, bool, 0444);
|
|||
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
|
||||
module_param_array(quirk_alias, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
|
||||
module_param_array(delayed_register, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4.");
|
||||
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
|
||||
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
|
||||
module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444);
|
||||
|
@ -525,6 +528,21 @@ static bool get_alias_id(struct usb_device *dev, unsigned int *id)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface)
|
||||
{
|
||||
int i;
|
||||
unsigned int id, inum;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(delayed_register); i++) {
|
||||
if (delayed_register[i] &&
|
||||
sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 &&
|
||||
id == chip->usb_id)
|
||||
return inum != iface;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct usb_device_id usb_audio_ids[]; /* defined below */
|
||||
|
||||
/* look for the corresponding quirk */
|
||||
|
@ -662,10 +680,22 @@ static int usb_audio_probe(struct usb_interface *intf,
|
|||
goto __error;
|
||||
}
|
||||
|
||||
/* we are allowed to call snd_card_register() many times */
|
||||
err = snd_card_register(chip->card);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
if (chip->need_delayed_register) {
|
||||
dev_info(&dev->dev,
|
||||
"Found post-registration device assignment: %08x:%02x\n",
|
||||
chip->usb_id, ifnum);
|
||||
chip->need_delayed_register = false; /* clear again */
|
||||
}
|
||||
|
||||
/* we are allowed to call snd_card_register() many times, but first
|
||||
* check to see if a device needs to skip it or do anything special
|
||||
*/
|
||||
if (!snd_usb_registration_quirk(chip, ifnum) &&
|
||||
!check_delayed_register_option(chip, ifnum)) {
|
||||
err = snd_card_register(chip->card);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
}
|
||||
|
||||
if (quirk && quirk->shares_media_device) {
|
||||
/* don't want to fail when snd_media_device_create() fails */
|
||||
|
|
|
@ -151,16 +151,15 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assume the clock is valid if clock source supports only one single sample
|
||||
* rate, the terminal is connected directly to it (there is no clock selector)
|
||||
* and clock type is internal. This is to deal with some Denon DJ controllers
|
||||
* that always reports that clock is invalid.
|
||||
*/
|
||||
static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
|
||||
struct audioformat *fmt,
|
||||
int source_id)
|
||||
{
|
||||
bool ret = false;
|
||||
int count;
|
||||
unsigned char data;
|
||||
struct usb_device *dev = chip->dev;
|
||||
|
||||
if (fmt->protocol == UAC_VERSION_2) {
|
||||
struct uac_clock_source_descriptor *cs_desc =
|
||||
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
|
||||
|
@ -168,13 +167,51 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
|
|||
if (!cs_desc)
|
||||
return false;
|
||||
|
||||
return (fmt->nr_rates == 1 &&
|
||||
(fmt->clock & 0xff) == cs_desc->bClockID &&
|
||||
(cs_desc->bmAttributes & 0x3) !=
|
||||
UAC_CLOCK_SOURCE_TYPE_EXT);
|
||||
/*
|
||||
* Assume the clock is valid if clock source supports only one
|
||||
* single sample rate, the terminal is connected directly to it
|
||||
* (there is no clock selector) and clock type is internal.
|
||||
* This is to deal with some Denon DJ controllers that always
|
||||
* reports that clock is invalid.
|
||||
*/
|
||||
if (fmt->nr_rates == 1 &&
|
||||
(fmt->clock & 0xff) == cs_desc->bClockID &&
|
||||
(cs_desc->bmAttributes & 0x3) !=
|
||||
UAC_CLOCK_SOURCE_TYPE_EXT)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
/*
|
||||
* MOTU MicroBook IIc
|
||||
* Sample rate changes takes more than 2 seconds for this device. Clock
|
||||
* validity request returns false during that period.
|
||||
*/
|
||||
if (chip->usb_id == USB_ID(0x07fd, 0x0004)) {
|
||||
count = 0;
|
||||
|
||||
while ((!ret) && (count < 50)) {
|
||||
int err;
|
||||
|
||||
msleep(100);
|
||||
|
||||
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_CLOCK_VALID << 8,
|
||||
snd_usb_ctrl_intf(chip) | (source_id << 8),
|
||||
&data, sizeof(data));
|
||||
if (err < 0) {
|
||||
dev_warn(&dev->dev,
|
||||
"%s(): cannot get clock validity for id %d\n",
|
||||
__func__, source_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = !!data;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
|
||||
|
|
|
@ -247,6 +247,36 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Presonus Studio 1810c supports a limited set of sampling
|
||||
* rates per altsetting but reports the full set each time.
|
||||
* If we don't filter out the unsupported rates and attempt
|
||||
* to configure the card, it will hang refusing to do any
|
||||
* further audio I/O until a hard reset is performed.
|
||||
*
|
||||
* The list of supported rates per altsetting (set of available
|
||||
* I/O channels) is described in the owner's manual, section 2.2.
|
||||
*/
|
||||
static bool s1810c_valid_sample_rate(struct audioformat *fp,
|
||||
unsigned int rate)
|
||||
{
|
||||
switch (fp->altsetting) {
|
||||
case 1:
|
||||
/* All ADAT ports available */
|
||||
return rate <= 48000;
|
||||
case 2:
|
||||
/* Half of ADAT ports available */
|
||||
return (rate == 88200 || rate == 96000);
|
||||
case 3:
|
||||
/* Analog I/O only (no S/PDIF nor ADAT) */
|
||||
return rate >= 176400;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to walk the array of sample rate triplets reported by
|
||||
* the device. The problem is that we need to parse whole array first to
|
||||
|
@ -283,6 +313,12 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
|
|||
}
|
||||
|
||||
for (rate = min; rate <= max; rate += res) {
|
||||
|
||||
/* Filter out invalid rates on Presonus Studio 1810c */
|
||||
if (chip->usb_id == USB_ID(0x0194f, 0x010c) &&
|
||||
!s1810c_valid_sample_rate(fp, rate))
|
||||
goto skip_rate;
|
||||
|
||||
if (fp->rate_table)
|
||||
fp->rate_table[nr_rates] = rate;
|
||||
if (!fp->rate_min || rate < fp->rate_min)
|
||||
|
@ -297,6 +333,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
|
|||
break;
|
||||
}
|
||||
|
||||
skip_rate:
|
||||
/* avoid endless loop */
|
||||
if (res == 0)
|
||||
break;
|
||||
|
|
|
@ -91,7 +91,7 @@ struct usb_ms_endpoint_descriptor {
|
|||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bNumEmbMIDIJack;
|
||||
__u8 baAssocJackID[0];
|
||||
__u8 baAssocJackID[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct snd_usb_midi_in_endpoint;
|
||||
|
|
|
@ -292,6 +292,11 @@ static int uac2_ctl_value_size(int val_type)
|
|||
* retrieve a mixer value
|
||||
*/
|
||||
|
||||
static inline int mixer_ctrl_intf(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
return get_iface_desc(mixer->hostif)->bInterfaceNumber;
|
||||
}
|
||||
|
||||
static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
|
||||
int validx, int *value_ret)
|
||||
{
|
||||
|
@ -306,7 +311,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
|
|||
return -EIO;
|
||||
|
||||
while (timeout-- > 0) {
|
||||
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
||||
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
|
||||
err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, idx, buf, val_len);
|
||||
|
@ -354,7 +359,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
|
|||
if (ret)
|
||||
goto error;
|
||||
|
||||
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
||||
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
|
||||
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, idx, buf, size);
|
||||
|
@ -479,7 +484,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
|
|||
return -EIO;
|
||||
|
||||
while (timeout-- > 0) {
|
||||
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
||||
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
|
||||
err = snd_usb_ctl_msg(chip->dev,
|
||||
usb_sndctrlpipe(chip->dev, 0), request,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
||||
|
@ -901,6 +906,12 @@ static int parse_term_effect_unit(struct mixer_build *state,
|
|||
struct usb_audio_term *term,
|
||||
void *p1, int id)
|
||||
{
|
||||
struct uac2_effect_unit_descriptor *d = p1;
|
||||
int err;
|
||||
|
||||
err = __check_input_term(state, d->bSourceID, term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
|
||||
term->id = id;
|
||||
return 0;
|
||||
|
@ -1203,7 +1214,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
|
|||
get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
|
||||
usb_audio_err(cval->head.mixer->chip,
|
||||
"%d:%d: cannot get min/max values for control %d (id %d)\n",
|
||||
cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip),
|
||||
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
|
||||
cval->control, cval->head.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1422,7 +1433,7 @@ static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
|
|||
if (ret)
|
||||
goto error;
|
||||
|
||||
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
||||
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
|
||||
if (cval->head.mixer->protocol == UAC_VERSION_2) {
|
||||
struct uac2_connectors_ctl_blk uac2_conn;
|
||||
|
||||
|
@ -1674,6 +1685,16 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
|||
/* get min/max values */
|
||||
get_min_max_with_quirks(cval, 0, kctl);
|
||||
|
||||
/* skip a bogus volume range */
|
||||
if (cval->max <= cval->min) {
|
||||
usb_audio_dbg(mixer->chip,
|
||||
"[%d] FU [%s] skipped due to invalid volume\n",
|
||||
cval->head.id, kctl->id.name);
|
||||
snd_ctl_free_one(kctl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (control == UAC_FU_VOLUME) {
|
||||
check_mapped_dB(map, cval);
|
||||
if (cval->dBmin < cval->dBmax || !cval->initialized) {
|
||||
|
@ -3203,7 +3224,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
|
|||
list_for_each_entry(mixer, &chip->mixer_list, list) {
|
||||
snd_iprintf(buffer,
|
||||
"USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n",
|
||||
chip->usb_id, snd_usb_ctrl_intf(chip),
|
||||
chip->usb_id, mixer_ctrl_intf(mixer),
|
||||
mixer->ignore_ctl_error);
|
||||
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
|
||||
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "mixer_scarlett.h"
|
||||
#include "mixer_scarlett_gen2.h"
|
||||
#include "mixer_us16x08.h"
|
||||
#include "mixer_s1810c.h"
|
||||
#include "helper.h"
|
||||
|
||||
struct std_mono_table {
|
||||
|
@ -2277,6 +2278,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|||
case USB_ID(0x2a39, 0x3fd4): /* RME */
|
||||
err = snd_rme_controls_create(mixer);
|
||||
break;
|
||||
|
||||
case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
|
||||
err = snd_sc1810_init_mixer(mixer);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -0,0 +1,595 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Presonus Studio 1810c driver for ALSA
|
||||
* Copyright (C) 2019 Nick Kossifidis <mickflemm@gmail.com>
|
||||
*
|
||||
* Based on reverse engineering of the communication protocol
|
||||
* between the windows driver / Univeral Control (UC) program
|
||||
* and the device, through usbmon.
|
||||
*
|
||||
* For now this bypasses the mixer, with all channels split,
|
||||
* so that the software can mix with greater flexibility.
|
||||
* It also adds controls for the 4 buttons on the front of
|
||||
* the device.
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/audio-v2.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "usbaudio.h"
|
||||
#include "mixer.h"
|
||||
#include "mixer_quirks.h"
|
||||
#include "helper.h"
|
||||
#include "mixer_s1810c.h"
|
||||
|
||||
#define SC1810C_CMD_REQ 160
|
||||
#define SC1810C_CMD_REQTYPE \
|
||||
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT)
|
||||
#define SC1810C_CMD_F1 0x50617269
|
||||
#define SC1810C_CMD_F2 0x14
|
||||
|
||||
/*
|
||||
* DISCLAIMER: These are just guesses based on the
|
||||
* dumps I got.
|
||||
*
|
||||
* It seems like a selects between
|
||||
* device (0), mixer (0x64) and output (0x65)
|
||||
*
|
||||
* For mixer (0x64):
|
||||
* * b selects an input channel (see below).
|
||||
* * c selects an output channel pair (see below).
|
||||
* * d selects left (0) or right (1) of that pair.
|
||||
* * e 0-> disconnect, 0x01000000-> connect,
|
||||
* 0x0109-> used for stereo-linking channels,
|
||||
* e is also used for setting volume levels
|
||||
* in which case b is also set so I guess
|
||||
* this way it is possible to set the volume
|
||||
* level from the specified input to the
|
||||
* specified output.
|
||||
*
|
||||
* IN Channels:
|
||||
* 0 - 7 Mic/Inst/Line (Analog inputs)
|
||||
* 8 - 9 S/PDIF
|
||||
* 10 - 17 ADAT
|
||||
* 18 - 35 DAW (Inputs from the host)
|
||||
*
|
||||
* OUT Channels (pairs):
|
||||
* 0 -> Main out
|
||||
* 1 -> Line1/2
|
||||
* 2 -> Line3/4
|
||||
* 3 -> S/PDIF
|
||||
* 4 -> ADAT?
|
||||
*
|
||||
* For device (0):
|
||||
* * b and c are not used, at least not on the
|
||||
* dumps I got.
|
||||
* * d sets the control id to be modified
|
||||
* (see below).
|
||||
* * e sets the setting for that control.
|
||||
* (so for the switches I was interested
|
||||
* in it's 0/1)
|
||||
*
|
||||
* For output (0x65):
|
||||
* * b is the output channel (see above).
|
||||
* * c is zero.
|
||||
* * e I guess the same as with mixer except 0x0109
|
||||
* which I didn't see in my dumps.
|
||||
*
|
||||
* The two fixed fields have the same values for
|
||||
* mixer and output but a different set for device.
|
||||
*/
|
||||
struct s1810c_ctl_packet {
|
||||
u32 a;
|
||||
u32 b;
|
||||
u32 fixed1;
|
||||
u32 fixed2;
|
||||
u32 c;
|
||||
u32 d;
|
||||
u32 e;
|
||||
};
|
||||
|
||||
#define SC1810C_CTL_LINE_SW 0
|
||||
#define SC1810C_CTL_MUTE_SW 1
|
||||
#define SC1810C_CTL_AB_SW 3
|
||||
#define SC1810C_CTL_48V_SW 4
|
||||
|
||||
#define SC1810C_SET_STATE_REQ 161
|
||||
#define SC1810C_SET_STATE_REQTYPE SC1810C_CMD_REQTYPE
|
||||
#define SC1810C_SET_STATE_F1 0x64656D73
|
||||
#define SC1810C_SET_STATE_F2 0xF4
|
||||
|
||||
#define SC1810C_GET_STATE_REQ 162
|
||||
#define SC1810C_GET_STATE_REQTYPE \
|
||||
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN)
|
||||
#define SC1810C_GET_STATE_F1 SC1810C_SET_STATE_F1
|
||||
#define SC1810C_GET_STATE_F2 SC1810C_SET_STATE_F2
|
||||
|
||||
#define SC1810C_STATE_F1_IDX 2
|
||||
#define SC1810C_STATE_F2_IDX 3
|
||||
|
||||
/*
|
||||
* This packet includes mixer volumes and
|
||||
* various other fields, it's an extended
|
||||
* version of ctl_packet, with a and b
|
||||
* being zero and different f1/f2.
|
||||
*/
|
||||
struct s1810c_state_packet {
|
||||
u32 fields[63];
|
||||
};
|
||||
|
||||
#define SC1810C_STATE_48V_SW 58
|
||||
#define SC1810C_STATE_LINE_SW 59
|
||||
#define SC1810C_STATE_MUTE_SW 60
|
||||
#define SC1810C_STATE_AB_SW 62
|
||||
|
||||
struct s1810_mixer_state {
|
||||
uint16_t seqnum;
|
||||
struct mutex usb_mutex;
|
||||
struct mutex data_mutex;
|
||||
};
|
||||
|
||||
static int
|
||||
snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a,
|
||||
u32 b, u32 c, u32 d, u32 e)
|
||||
{
|
||||
struct s1810c_ctl_packet pkt = { 0 };
|
||||
int ret = 0;
|
||||
|
||||
pkt.fixed1 = SC1810C_CMD_F1;
|
||||
pkt.fixed2 = SC1810C_CMD_F2;
|
||||
|
||||
pkt.a = a;
|
||||
pkt.b = b;
|
||||
pkt.c = c;
|
||||
pkt.d = d;
|
||||
/*
|
||||
* Value for settings 0/1 for this
|
||||
* output channel is always 0 (probably because
|
||||
* there is no ADAT output on 1810c)
|
||||
*/
|
||||
pkt.e = (c == 4) ? 0 : e;
|
||||
|
||||
ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
SC1810C_CMD_REQ,
|
||||
SC1810C_CMD_REQTYPE, 0, 0, &pkt, sizeof(pkt));
|
||||
if (ret < 0) {
|
||||
dev_warn(&dev->dev, "could not send ctl packet\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When opening Universal Control the program periodicaly
|
||||
* sends and receives state packets for syncinc state between
|
||||
* the device and the host.
|
||||
*
|
||||
* Note that if we send only the request to get data back we'll
|
||||
* get an error, we need to first send an empty state packet and
|
||||
* then ask to receive a filled. Their seqnumbers must also match.
|
||||
*/
|
||||
static int
|
||||
snd_sc1810c_get_status_field(struct usb_device *dev,
|
||||
u32 *field, int field_idx, uint16_t *seqnum)
|
||||
{
|
||||
struct s1810c_state_packet pkt_out = { { 0 } };
|
||||
struct s1810c_state_packet pkt_in = { { 0 } };
|
||||
int ret = 0;
|
||||
|
||||
pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1;
|
||||
pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2;
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
SC1810C_SET_STATE_REQ,
|
||||
SC1810C_SET_STATE_REQTYPE,
|
||||
(*seqnum), 0, &pkt_out, sizeof(pkt_out));
|
||||
if (ret < 0) {
|
||||
dev_warn(&dev->dev, "could not send state packet (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
SC1810C_GET_STATE_REQ,
|
||||
SC1810C_GET_STATE_REQTYPE,
|
||||
(*seqnum), 0, &pkt_in, sizeof(pkt_in));
|
||||
if (ret < 0) {
|
||||
dev_warn(&dev->dev, "could not get state field %u (%d)\n",
|
||||
field_idx, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*field) = pkt_in.fields[field_idx];
|
||||
(*seqnum)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is what I got when bypassing the mixer with
|
||||
* all channels split. I'm not 100% sure of what's going
|
||||
* on, I could probably clean this up based on my observations
|
||||
* but I prefer to keep the same behavior as the windows driver.
|
||||
*/
|
||||
static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
|
||||
{
|
||||
u32 a, b, c, e, n, off;
|
||||
struct usb_device *dev = chip->dev;
|
||||
|
||||
/* Set initial volume levels ? */
|
||||
a = 0x64;
|
||||
e = 0xbc;
|
||||
for (n = 0; n < 2; n++) {
|
||||
off = n * 18;
|
||||
for (b = off, c = 0; b < 18 + off; b++) {
|
||||
/* This channel to all outputs ? */
|
||||
for (c = 0; c <= 8; c++) {
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
|
||||
}
|
||||
/* This channel to main output (again) */
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
|
||||
}
|
||||
/*
|
||||
* I noticed on UC that DAW channels have different
|
||||
* initial volumes, so this makes sense.
|
||||
*/
|
||||
e = 0xb53bf0;
|
||||
}
|
||||
|
||||
/* Connect analog outputs ? */
|
||||
a = 0x65;
|
||||
e = 0x01000000;
|
||||
for (b = 1; b < 3; b++) {
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
|
||||
}
|
||||
snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e);
|
||||
|
||||
/* Set initial volume levels for S/PDIF mappings ? */
|
||||
a = 0x64;
|
||||
e = 0xbc;
|
||||
c = 3;
|
||||
for (n = 0; n < 2; n++) {
|
||||
off = n * 18;
|
||||
for (b = off; b < 18 + off; b++) {
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
|
||||
}
|
||||
e = 0xb53bf0;
|
||||
}
|
||||
|
||||
/* Connect S/PDIF output ? */
|
||||
a = 0x65;
|
||||
e = 0x01000000;
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
|
||||
|
||||
/* Connect all outputs (again) ? */
|
||||
a = 0x65;
|
||||
e = 0x01000000;
|
||||
for (b = 0; b < 4; b++) {
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
|
||||
}
|
||||
|
||||
/* Basic routing to get sound out of the device */
|
||||
a = 0x64;
|
||||
e = 0x01000000;
|
||||
for (c = 0; c < 4; c++) {
|
||||
for (b = 0; b < 36; b++) {
|
||||
if ((c == 0 && b == 18) || /* DAW1/2 -> Main */
|
||||
(c == 1 && b == 20) || /* DAW3/4 -> Line3/4 */
|
||||
(c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */
|
||||
(c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */
|
||||
/* Left */
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
|
||||
b++;
|
||||
/* Right */
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
|
||||
} else {
|
||||
/* Leave the rest disconnected */
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set initial volume levels for S/PDIF (again) ? */
|
||||
a = 0x64;
|
||||
e = 0xbc;
|
||||
c = 3;
|
||||
for (n = 0; n < 2; n++) {
|
||||
off = n * 18;
|
||||
for (b = off; b < 18 + off; b++) {
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
|
||||
}
|
||||
e = 0xb53bf0;
|
||||
}
|
||||
|
||||
/* Connect S/PDIF outputs (again) ? */
|
||||
a = 0x65;
|
||||
e = 0x01000000;
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
|
||||
|
||||
/* Again ? */
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync state with the device and retrieve the requested field,
|
||||
* whose index is specified in (kctl->private_value & 0xFF),
|
||||
* from the received fields array.
|
||||
*/
|
||||
static int
|
||||
snd_s1810c_get_switch_state(struct usb_mixer_interface *mixer,
|
||||
struct snd_kcontrol *kctl, u32 *state)
|
||||
{
|
||||
struct snd_usb_audio *chip = mixer->chip;
|
||||
struct s1810_mixer_state *private = mixer->private_data;
|
||||
u32 field = 0;
|
||||
u32 ctl_idx = (u32) (kctl->private_value & 0xFF);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&private->usb_mutex);
|
||||
ret = snd_sc1810c_get_status_field(chip->dev, &field,
|
||||
ctl_idx, &private->seqnum);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
*state = field;
|
||||
unlock:
|
||||
mutex_unlock(&private->usb_mutex);
|
||||
return ret ? ret : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a control packet to the device for the control id
|
||||
* specified in (kctl->private_value >> 8) with value
|
||||
* specified in (kctl->private_value >> 16).
|
||||
*/
|
||||
static int
|
||||
snd_s1810c_set_switch_state(struct usb_mixer_interface *mixer,
|
||||
struct snd_kcontrol *kctl)
|
||||
{
|
||||
struct snd_usb_audio *chip = mixer->chip;
|
||||
struct s1810_mixer_state *private = mixer->private_data;
|
||||
u32 pval = (u32) kctl->private_value;
|
||||
u32 ctl_id = (pval >> 8) & 0xFF;
|
||||
u32 ctl_val = (pval >> 16) & 0x1;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&private->usb_mutex);
|
||||
ret = snd_s1810c_send_ctl_packet(chip->dev, 0, 0, 0, ctl_id, ctl_val);
|
||||
mutex_unlock(&private->usb_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Generic get/set/init functions for switch controls */
|
||||
|
||||
static int
|
||||
snd_s1810c_switch_get(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *ctl_elem)
|
||||
{
|
||||
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
|
||||
struct usb_mixer_interface *mixer = list->mixer;
|
||||
struct s1810_mixer_state *private = mixer->private_data;
|
||||
u32 pval = (u32) kctl->private_value;
|
||||
u32 ctl_idx = pval & 0xFF;
|
||||
u32 state = 0;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&private->data_mutex);
|
||||
ret = snd_s1810c_get_switch_state(mixer, kctl, &state);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
switch (ctl_idx) {
|
||||
case SC1810C_STATE_LINE_SW:
|
||||
case SC1810C_STATE_AB_SW:
|
||||
ctl_elem->value.enumerated.item[0] = (int)state;
|
||||
break;
|
||||
default:
|
||||
ctl_elem->value.integer.value[0] = (long)state;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&private->data_mutex);
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
snd_s1810c_switch_set(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *ctl_elem)
|
||||
{
|
||||
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
|
||||
struct usb_mixer_interface *mixer = list->mixer;
|
||||
struct s1810_mixer_state *private = mixer->private_data;
|
||||
u32 pval = (u32) kctl->private_value;
|
||||
u32 ctl_idx = pval & 0xFF;
|
||||
u32 curval = 0;
|
||||
u32 newval = 0;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&private->data_mutex);
|
||||
ret = snd_s1810c_get_switch_state(mixer, kctl, &curval);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
switch (ctl_idx) {
|
||||
case SC1810C_STATE_LINE_SW:
|
||||
case SC1810C_STATE_AB_SW:
|
||||
newval = (u32) ctl_elem->value.enumerated.item[0];
|
||||
break;
|
||||
default:
|
||||
newval = (u32) ctl_elem->value.integer.value[0];
|
||||
}
|
||||
|
||||
if (curval == newval)
|
||||
goto unlock;
|
||||
|
||||
kctl->private_value &= ~(0x1 << 16);
|
||||
kctl->private_value |= (unsigned int)(newval & 0x1) << 16;
|
||||
ret = snd_s1810c_set_switch_state(mixer, kctl);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&private->data_mutex);
|
||||
return (ret < 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
snd_s1810c_switch_init(struct usb_mixer_interface *mixer,
|
||||
const struct snd_kcontrol_new *new_kctl)
|
||||
{
|
||||
struct snd_kcontrol *kctl;
|
||||
struct usb_mixer_elem_info *elem;
|
||||
|
||||
elem = kzalloc(sizeof(struct usb_mixer_elem_info), GFP_KERNEL);
|
||||
if (!elem)
|
||||
return -ENOMEM;
|
||||
|
||||
elem->head.mixer = mixer;
|
||||
elem->control = 0;
|
||||
elem->head.id = 0;
|
||||
elem->channels = 1;
|
||||
|
||||
kctl = snd_ctl_new1(new_kctl, elem);
|
||||
if (!kctl) {
|
||||
kfree(elem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
kctl->private_free = snd_usb_mixer_elem_free;
|
||||
|
||||
return snd_usb_mixer_add_control(&elem->head, kctl);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_s1810c_line_sw_info(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static const char *const texts[2] = {
|
||||
"Preamp On (Mic/Inst)",
|
||||
"Preamp Off (Line in)"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_s1810c_line_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Line 1/2 Source Type",
|
||||
.info = snd_s1810c_line_sw_info,
|
||||
.get = snd_s1810c_switch_get,
|
||||
.put = snd_s1810c_switch_set,
|
||||
.private_value = (SC1810C_STATE_LINE_SW | SC1810C_CTL_LINE_SW << 8)
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new snd_s1810c_mute_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Mute Main Out Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = snd_s1810c_switch_get,
|
||||
.put = snd_s1810c_switch_set,
|
||||
.private_value = (SC1810C_STATE_MUTE_SW | SC1810C_CTL_MUTE_SW << 8)
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new snd_s1810c_48v_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "48V Phantom Power On Mic Inputs Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = snd_s1810c_switch_get,
|
||||
.put = snd_s1810c_switch_set,
|
||||
.private_value = (SC1810C_STATE_48V_SW | SC1810C_CTL_48V_SW << 8)
|
||||
};
|
||||
|
||||
static int
|
||||
snd_s1810c_ab_sw_info(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static const char *const texts[2] = {
|
||||
"1/2",
|
||||
"3/4"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_s1810c_ab_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone 1 Source Route",
|
||||
.info = snd_s1810c_ab_sw_info,
|
||||
.get = snd_s1810c_switch_get,
|
||||
.put = snd_s1810c_switch_set,
|
||||
.private_value = (SC1810C_STATE_AB_SW | SC1810C_CTL_AB_SW << 8)
|
||||
};
|
||||
|
||||
static void snd_sc1810_mixer_state_free(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
struct s1810_mixer_state *private = mixer->private_data;
|
||||
kfree(private);
|
||||
mixer->private_data = NULL;
|
||||
}
|
||||
|
||||
/* Entry point, called from mixer_quirks.c */
|
||||
int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
struct s1810_mixer_state *private = NULL;
|
||||
struct snd_usb_audio *chip = mixer->chip;
|
||||
struct usb_device *dev = chip->dev;
|
||||
int ret = 0;
|
||||
|
||||
/* Run this only once */
|
||||
if (!list_empty(&chip->mixer_list))
|
||||
return 0;
|
||||
|
||||
dev_info(&dev->dev,
|
||||
"Presonus Studio 1810c, device_setup: %u\n", chip->setup);
|
||||
if (chip->setup == 1)
|
||||
dev_info(&dev->dev, "(8out/18in @ 48KHz)\n");
|
||||
else if (chip->setup == 2)
|
||||
dev_info(&dev->dev, "(6out/8in @ 192KHz)\n");
|
||||
else
|
||||
dev_info(&dev->dev, "(8out/14in @ 96KHz)\n");
|
||||
|
||||
ret = snd_s1810c_init_mixer_maps(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
private = kzalloc(sizeof(struct s1810_mixer_state), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&private->usb_mutex);
|
||||
mutex_init(&private->data_mutex);
|
||||
|
||||
mixer->private_data = private;
|
||||
mixer->private_free = snd_sc1810_mixer_state_free;
|
||||
|
||||
private->seqnum = 1;
|
||||
|
||||
ret = snd_s1810c_switch_init(mixer, &snd_s1810c_line_sw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_s1810c_switch_init(mixer, &snd_s1810c_mute_sw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_s1810c_switch_init(mixer, &snd_s1810c_48v_sw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_s1810c_switch_init(mixer, &snd_s1810c_ab_sw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Presonus Studio 1810c driver for ALSA
|
||||
* Copyright (C) 2019 Nick Kossifidis <mickflemm@gmail.com>
|
||||
*/
|
||||
|
||||
int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer);
|
|
@ -357,7 +357,12 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
|
|||
ep = 0x81;
|
||||
ifnum = 1;
|
||||
goto add_sync_ep_from_ifnum;
|
||||
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
|
||||
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */
|
||||
/* MicroBook IIc */
|
||||
if (altsd->bInterfaceClass == USB_CLASS_AUDIO)
|
||||
return 0;
|
||||
|
||||
/* MicroBook II */
|
||||
ep = 0x84;
|
||||
ifnum = 0;
|
||||
goto add_sync_ep_from_ifnum;
|
||||
|
|
|
@ -70,7 +70,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
|
|||
snd_iprintf(buffer, " Interface %d\n", fp->iface);
|
||||
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
|
||||
snd_iprintf(buffer, " Format:");
|
||||
for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt)
|
||||
pcm_for_each_format(fmt)
|
||||
if (fp->formats & pcm_format_to_bits(fmt))
|
||||
snd_iprintf(buffer, " %s",
|
||||
snd_pcm_format_name(fmt));
|
||||
|
|
|
@ -3472,7 +3472,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
},
|
||||
/* MOTU Microbook II */
|
||||
{
|
||||
USB_DEVICE(0x07fd, 0x0004),
|
||||
USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004),
|
||||
.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
|
||||
.vendor_name = "MOTU",
|
||||
.product_name = "MicroBookII",
|
||||
|
|
|
@ -1252,6 +1252,38 @@ static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip,
|
|||
return 0; /* keep this altsetting */
|
||||
}
|
||||
|
||||
static int s1810c_skip_setting_quirk(struct snd_usb_audio *chip,
|
||||
int iface, int altno)
|
||||
{
|
||||
/*
|
||||
* Altno settings:
|
||||
*
|
||||
* Playback (Interface 1):
|
||||
* 1: 6 Analog + 2 S/PDIF
|
||||
* 2: 6 Analog + 2 S/PDIF
|
||||
* 3: 6 Analog
|
||||
*
|
||||
* Capture (Interface 2):
|
||||
* 1: 8 Analog + 2 S/PDIF + 8 ADAT
|
||||
* 2: 8 Analog + 2 S/PDIF + 4 ADAT
|
||||
* 3: 8 Analog
|
||||
*/
|
||||
|
||||
/*
|
||||
* I'll leave 2 as the default one and
|
||||
* use device_setup to switch to the
|
||||
* other two.
|
||||
*/
|
||||
if ((chip->setup == 0 || chip->setup > 2) && altno != 2)
|
||||
return 1;
|
||||
else if (chip->setup == 1 && altno != 1)
|
||||
return 1;
|
||||
else if (chip->setup == 2 && altno != 3)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
|
||||
int iface,
|
||||
int altno)
|
||||
|
@ -1265,6 +1297,10 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
|
|||
/* fasttrackpro usb: skip altsets incompatible with device_setup */
|
||||
if (chip->usb_id == USB_ID(0x0763, 0x2012))
|
||||
return fasttrackpro_skip_setting_quirk(chip, iface, altno);
|
||||
/* presonus studio 1810c: skip altsets incompatible with device_setup */
|
||||
if (chip->usb_id == USB_ID(0x0194f, 0x010c))
|
||||
return s1810c_skip_setting_quirk(chip, iface, altno);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1316,7 +1352,15 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
|
|||
case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
|
||||
return snd_usb_axefx3_boot_quirk(dev);
|
||||
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
|
||||
return snd_usb_motu_microbookii_boot_quirk(dev);
|
||||
/*
|
||||
* For some reason interface 3 with vendor-spec class is
|
||||
* detected on MicroBook IIc.
|
||||
*/
|
||||
if (get_iface_desc(intf->altsetting)->bInterfaceClass ==
|
||||
USB_CLASS_VENDOR_SPEC &&
|
||||
get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
|
||||
return snd_usb_motu_microbookii_boot_quirk(dev);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1754,5 +1798,47 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
|||
else
|
||||
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
||||
break;
|
||||
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook IIc */
|
||||
/*
|
||||
* MaxPacketsOnly attribute is erroneously set in endpoint
|
||||
* descriptors. As a result this card produces noise with
|
||||
* all sample rates other than 96 KHz.
|
||||
*/
|
||||
fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* registration quirk:
|
||||
* the registration is skipped if a device matches with the given ID,
|
||||
* unless the interface reaches to the defined one. This is for delaying
|
||||
* the registration until the last known interface, so that the card and
|
||||
* devices appear at the same time.
|
||||
*/
|
||||
|
||||
struct registration_quirk {
|
||||
unsigned int usb_id; /* composed via USB_ID() */
|
||||
unsigned int interface; /* the interface to trigger register */
|
||||
};
|
||||
|
||||
#define REG_QUIRK_ENTRY(vendor, product, iface) \
|
||||
{ .usb_id = USB_ID(vendor, product), .interface = (iface) }
|
||||
|
||||
static const struct registration_quirk registration_quirks[] = {
|
||||
REG_QUIRK_ENTRY(0x0951, 0x16d8, 2), /* Kingston HyperX AMP */
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
/* return true if skipping registration */
|
||||
bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface)
|
||||
{
|
||||
const struct registration_quirk *q;
|
||||
|
||||
for (q = registration_quirks; q->usb_id; q++)
|
||||
if (chip->usb_id == q->usb_id)
|
||||
return iface != q->interface;
|
||||
|
||||
/* Register as normal */
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -51,4 +51,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
|||
struct audioformat *fp,
|
||||
int stream);
|
||||
|
||||
bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface);
|
||||
|
||||
#endif /* __USBAUDIO_QUIRKS_H */
|
||||
|
|
|
@ -502,6 +502,9 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
|||
subs = &as->substream[stream];
|
||||
if (subs->ep_num)
|
||||
continue;
|
||||
if (snd_device_get_state(chip->card, as->pcm) !=
|
||||
SNDRV_DEV_BUILD)
|
||||
chip->need_delayed_register = true;
|
||||
err = snd_pcm_new_stream(as->pcm, stream, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
|
@ -34,6 +34,7 @@ struct snd_usb_audio {
|
|||
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
|
||||
unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
|
||||
unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */
|
||||
unsigned int need_delayed_register:1; /* warn for delayed registration */
|
||||
int num_interfaces;
|
||||
int num_suspended_intf;
|
||||
int sample_rate_read_error;
|
||||
|
|
Loading…
Reference in New Issue