mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 18:22:00 -04:00
Merge branch 'for-next' into for-linus
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
@@ -2376,6 +2376,13 @@ quirk_flags
|
||||
Skip the probe-time interface setup (usb_set_interface,
|
||||
init_pitch, init_sample_rate); redundant with
|
||||
snd_usb_endpoint_prepare() at stream-open time
|
||||
* bit 27: ``mixer_playback_linear_vol``
|
||||
Set linear volume mapping for devices where the playback volume
|
||||
control value is mapped to voltage (instead of dB) level linearly.
|
||||
In short: ``x(raw) = (raw - raw_min) / (raw_max - raw_min)``;
|
||||
``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24
|
||||
* bit 28: ``mixer_capture_linear_vol``
|
||||
Similar to bit 27 but for capture streams. Overrides bit 25
|
||||
|
||||
This module supports multiple devices, autoprobe and hotplugging.
|
||||
|
||||
|
||||
@@ -133,6 +133,9 @@ struct snd_card {
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
struct dentry *debugfs_root; /* debugfs root for card */
|
||||
#endif
|
||||
#ifdef CONFIG_SND_CTL_DEBUG
|
||||
struct snd_ctl_elem_value *value_buf; /* buffer for kctl->put() verification */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_state; /* power state */
|
||||
|
||||
@@ -536,6 +536,7 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
|
||||
struct snd_gf1_dma_block * block,
|
||||
int atomic,
|
||||
int synth);
|
||||
void snd_gf1_dma_suspend(struct snd_gus_card *gus);
|
||||
|
||||
/* gus_volume.c */
|
||||
|
||||
@@ -552,6 +553,8 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i
|
||||
void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice);
|
||||
int snd_gf1_start(struct snd_gus_card * gus);
|
||||
int snd_gf1_stop(struct snd_gus_card * gus);
|
||||
int snd_gf1_suspend(struct snd_gus_card *gus);
|
||||
int snd_gf1_resume(struct snd_gus_card *gus);
|
||||
|
||||
/* gus_mixer.c */
|
||||
|
||||
@@ -572,6 +575,8 @@ int snd_gus_create(struct snd_card *card,
|
||||
int effect,
|
||||
struct snd_gus_card ** rgus);
|
||||
int snd_gus_initialize(struct snd_gus_card * gus);
|
||||
int snd_gus_suspend(struct snd_gus_card *gus);
|
||||
int snd_gus_resume(struct snd_gus_card *gus);
|
||||
|
||||
/* gus_irq.c */
|
||||
|
||||
@@ -583,6 +588,8 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus);
|
||||
/* gus_uart.c */
|
||||
|
||||
int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device);
|
||||
void snd_gf1_uart_suspend(struct snd_gus_card *gus);
|
||||
void snd_gf1_uart_resume(struct snd_gus_card *gus);
|
||||
|
||||
/* gus_dram.c */
|
||||
int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr,
|
||||
@@ -593,5 +600,6 @@ int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr,
|
||||
/* gus_timer.c */
|
||||
void snd_gf1_timers_init(struct snd_gus_card *gus);
|
||||
void snd_gf1_timers_done(struct snd_gus_card *gus);
|
||||
void snd_gf1_timers_resume(struct snd_gus_card *gus);
|
||||
|
||||
#endif /* __SOUND_GUS_H */
|
||||
|
||||
@@ -336,6 +336,17 @@ snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
|
||||
}
|
||||
|
||||
/* sync after write */
|
||||
static inline int
|
||||
snd_hda_codec_write_sync(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
/* use snd_hda_codec_read() for writing;
|
||||
* the returned value is usually discarded
|
||||
*/
|
||||
return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
|
||||
}
|
||||
|
||||
#define snd_hda_param_read(codec, nid, param) \
|
||||
snd_hdac_read_parm(&(codec)->core, nid, param)
|
||||
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
|
||||
@@ -470,6 +481,10 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
|
||||
void snd_hda_bus_reset(struct hda_bus *bus);
|
||||
void snd_hda_bus_reset_codecs(struct hda_bus *bus);
|
||||
|
||||
void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask,
|
||||
unsigned int dir, unsigned int data,
|
||||
unsigned int delay);
|
||||
|
||||
int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
|
||||
|
||||
/*
|
||||
|
||||
@@ -56,7 +56,12 @@ enum {
|
||||
#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
|
||||
#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
|
||||
#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
|
||||
/* f10-f1a: GPIO */
|
||||
/* f10-f1a: GPI/GPO/GPIO */
|
||||
#define AC_VERB_GET_GPI_DATA 0x0f10
|
||||
#define AC_VERB_GET_GPI_WAKE_MASK 0x0f11
|
||||
#define AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK 0x0f12
|
||||
#define AC_VERB_GET_GPI_STICKY_MASK 0x0f13
|
||||
#define AC_VERB_GET_GPO_DATA 0x0f14
|
||||
#define AC_VERB_GET_GPIO_DATA 0x0f15
|
||||
#define AC_VERB_GET_GPIO_MASK 0x0f16
|
||||
#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
|
||||
@@ -99,6 +104,11 @@ enum {
|
||||
#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
|
||||
#define AC_VERB_SET_DIGI_CONVERT_3 0x73e
|
||||
#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
|
||||
#define AC_VERB_SET_GPI_DATA 0x710
|
||||
#define AC_VERB_SET_GPI_WAKE_MASK 0x711
|
||||
#define AC_VERB_SET_SPI_UNSOLICITED_RSP_MASK 0x712
|
||||
#define AC_VERB_SET_GPI_STICKY_MASK 0x713
|
||||
#define AC_VERB_SET_GPO_DATA 0x714
|
||||
#define AC_VERB_SET_GPIO_DATA 0x715
|
||||
#define AC_VERB_SET_GPIO_MASK 0x716
|
||||
#define AC_VERB_SET_GPIO_DIRECTION 0x717
|
||||
|
||||
@@ -729,6 +729,10 @@ static inline void __snd_pcm_set_state(struct snd_pcm_runtime *runtime,
|
||||
runtime->status->state = state; /* copy for mmap */
|
||||
}
|
||||
|
||||
void snd_pcm_set_state(struct snd_pcm_substream *substream,
|
||||
snd_pcm_state_t state);
|
||||
snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream);
|
||||
|
||||
/**
|
||||
* bytes_to_samples - Unit conversion of the size from bytes to samples
|
||||
* @runtime: PCM runtime instance
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer);
|
||||
int snd_tea6330t_update_mixer(struct snd_card *card, struct snd_i2c_bus *bus,
|
||||
int equalizer, int fader);
|
||||
int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus);
|
||||
|
||||
#endif /* __SOUND_TEA6330T_H */
|
||||
|
||||
@@ -102,6 +102,7 @@ struct snd_timer_instance {
|
||||
unsigned int slave_id;
|
||||
struct list_head open_list;
|
||||
struct list_head active_list;
|
||||
struct list_head master_list;
|
||||
struct list_head ack_list;
|
||||
struct list_head slave_list_head;
|
||||
struct list_head slave_active_head;
|
||||
|
||||
@@ -48,7 +48,7 @@ struct aoa_codec {
|
||||
u32 connected;
|
||||
|
||||
/* data the fabric can associate with this structure */
|
||||
void *fabric_data;
|
||||
const void *fabric_data;
|
||||
|
||||
/* private! */
|
||||
struct list_head list;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/asoundef.h>
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
|
||||
@@ -514,8 +515,36 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int onyx_set_spdif_pcm_rate(struct onyx *onyx, unsigned int rate)
|
||||
{
|
||||
u8 dig_info3, fs;
|
||||
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
fs = IEC958_AES3_CON_FS_32000;
|
||||
break;
|
||||
case 44100:
|
||||
fs = IEC958_AES3_CON_FS_44100;
|
||||
break;
|
||||
case 48000:
|
||||
fs = IEC958_AES3_CON_FS_48000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &dig_info3))
|
||||
return -EBUSY;
|
||||
dig_info3 = (dig_info3 & ~IEC958_AES3_CON_FS) | fs;
|
||||
if (onyx_write_register(onyx, ONYX_REG_DIG_INFO3, dig_info3))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new onyx_spdif_ctrl = {
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
||||
.info = onyx_spdif_info,
|
||||
@@ -695,9 +724,9 @@ static int onyx_prepare(struct codec_info_item *cii,
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
/* these rates are ok for all outputs */
|
||||
/* FIXME: program spdif channel control bits here so that
|
||||
* userspace doesn't have to if it only plays pcm! */
|
||||
if (onyx->codec.connected & 2)
|
||||
return onyx_set_spdif_pcm_rate(onyx,
|
||||
substream->runtime->rate);
|
||||
return 0;
|
||||
default:
|
||||
/* got some rate that the digital output can't do,
|
||||
@@ -980,10 +1009,12 @@ static int onyx_i2c_probe(struct i2c_client *client)
|
||||
onyx->codec.node = of_node_get(node);
|
||||
|
||||
if (aoa_codec_register(&onyx->codec)) {
|
||||
goto fail;
|
||||
goto fail_put;
|
||||
}
|
||||
printk(KERN_DEBUG PFX "created and attached onyx instance\n");
|
||||
return 0;
|
||||
fail_put:
|
||||
of_node_put(onyx->codec.node);
|
||||
fail:
|
||||
kfree(onyx);
|
||||
return -ENODEV;
|
||||
|
||||
@@ -872,6 +872,7 @@ static int tas_i2c_probe(struct i2c_client *client)
|
||||
return 0;
|
||||
fail:
|
||||
mutex_destroy(&tas->mtx);
|
||||
of_node_put(tas->codec.node);
|
||||
kfree(tas);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ struct codec_connection {
|
||||
|
||||
struct codec_connect_info {
|
||||
char *name;
|
||||
struct codec_connection *connections;
|
||||
const struct codec_connection *connections;
|
||||
};
|
||||
|
||||
#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
|
||||
@@ -116,7 +116,7 @@ MODULE_ALIAS("aoa-device-id-35");
|
||||
MODULE_ALIAS("aoa-device-id-44");
|
||||
|
||||
/* onyx with all but microphone connected */
|
||||
static struct codec_connection onyx_connections_nomic[] = {
|
||||
static const struct codec_connection onyx_connections_nomic[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
|
||||
.codec_bit = 0,
|
||||
@@ -133,7 +133,7 @@ static struct codec_connection onyx_connections_nomic[] = {
|
||||
};
|
||||
|
||||
/* onyx on machines without headphone */
|
||||
static struct codec_connection onyx_connections_noheadphones[] = {
|
||||
static const struct codec_connection onyx_connections_noheadphones[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_LINEOUT |
|
||||
CC_LINEOUT_LABELLED_HEADPHONE,
|
||||
@@ -157,7 +157,7 @@ static struct codec_connection onyx_connections_noheadphones[] = {
|
||||
};
|
||||
|
||||
/* onyx on machines with real line-out */
|
||||
static struct codec_connection onyx_connections_reallineout[] = {
|
||||
static const struct codec_connection onyx_connections_reallineout[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
|
||||
.codec_bit = 0,
|
||||
@@ -174,7 +174,7 @@ static struct codec_connection onyx_connections_reallineout[] = {
|
||||
};
|
||||
|
||||
/* tas on machines without line out */
|
||||
static struct codec_connection tas_connections_nolineout[] = {
|
||||
static const struct codec_connection tas_connections_nolineout[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE,
|
||||
.codec_bit = 0,
|
||||
@@ -191,7 +191,7 @@ static struct codec_connection tas_connections_nolineout[] = {
|
||||
};
|
||||
|
||||
/* tas on machines with neither line out nor line in */
|
||||
static struct codec_connection tas_connections_noline[] = {
|
||||
static const struct codec_connection tas_connections_noline[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE,
|
||||
.codec_bit = 0,
|
||||
@@ -204,7 +204,7 @@ static struct codec_connection tas_connections_noline[] = {
|
||||
};
|
||||
|
||||
/* tas on machines without microphone */
|
||||
static struct codec_connection tas_connections_nomic[] = {
|
||||
static const struct codec_connection tas_connections_nomic[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
|
||||
.codec_bit = 0,
|
||||
@@ -217,7 +217,7 @@ static struct codec_connection tas_connections_nomic[] = {
|
||||
};
|
||||
|
||||
/* tas on machines with everything connected */
|
||||
static struct codec_connection tas_connections_all[] = {
|
||||
static const struct codec_connection tas_connections_all[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
|
||||
.codec_bit = 0,
|
||||
@@ -233,7 +233,7 @@ static struct codec_connection tas_connections_all[] = {
|
||||
{} /* terminate array by .connected == 0 */
|
||||
};
|
||||
|
||||
static struct codec_connection toonie_connections[] = {
|
||||
static const struct codec_connection toonie_connections[] = {
|
||||
{
|
||||
.connected = CC_SPEAKERS | CC_HEADPHONE,
|
||||
.codec_bit = 0,
|
||||
@@ -241,7 +241,7 @@ static struct codec_connection toonie_connections[] = {
|
||||
{} /* terminate array by .connected == 0 */
|
||||
};
|
||||
|
||||
static struct codec_connection topaz_input[] = {
|
||||
static const struct codec_connection topaz_input[] = {
|
||||
{
|
||||
.connected = CC_DIGITALIN,
|
||||
.codec_bit = 0,
|
||||
@@ -249,7 +249,7 @@ static struct codec_connection topaz_input[] = {
|
||||
{} /* terminate array by .connected == 0 */
|
||||
};
|
||||
|
||||
static struct codec_connection topaz_output[] = {
|
||||
static const struct codec_connection topaz_output[] = {
|
||||
{
|
||||
.connected = CC_DIGITALOUT,
|
||||
.codec_bit = 1,
|
||||
@@ -257,7 +257,7 @@ static struct codec_connection topaz_output[] = {
|
||||
{} /* terminate array by .connected == 0 */
|
||||
};
|
||||
|
||||
static struct codec_connection topaz_inout[] = {
|
||||
static const struct codec_connection topaz_inout[] = {
|
||||
{
|
||||
.connected = CC_DIGITALIN,
|
||||
.codec_bit = 0,
|
||||
@@ -772,7 +772,7 @@ static int check_codec(struct aoa_codec *codec,
|
||||
{
|
||||
const u32 *ref;
|
||||
char propname[32];
|
||||
struct codec_connection *cc;
|
||||
const struct codec_connection *cc;
|
||||
|
||||
/* if the codec has a 'codec' node, we require a reference */
|
||||
if (of_node_name_eq(codec->node, "codec")) {
|
||||
@@ -895,7 +895,7 @@ static void layout_notify(void *data)
|
||||
|
||||
static void layout_attached_codec(struct aoa_codec *codec)
|
||||
{
|
||||
struct codec_connection *cc;
|
||||
const struct codec_connection *cc;
|
||||
struct snd_kcontrol *ctl;
|
||||
int headphones, lineout;
|
||||
struct layout_dev *ldev = layout_device;
|
||||
|
||||
@@ -84,6 +84,7 @@ static void i2sbus_release_dev(struct device *dev)
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
|
||||
free_irq(i2sdev->interrupts[i], i2sdev);
|
||||
i2sbus_control_remove_dev(i2sdev->control, i2sdev);
|
||||
of_node_put(i2sdev->sound.ofdev.dev.of_node);
|
||||
mutex_destroy(&i2sdev->lock);
|
||||
kfree(i2sdev);
|
||||
}
|
||||
@@ -147,7 +148,6 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
|
||||
}
|
||||
|
||||
/* Returns 1 if added, 0 for otherwise; don't return a negative value! */
|
||||
/* FIXME: look at device node refcounting */
|
||||
static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
struct i2sbus_control *control,
|
||||
struct device_node *np)
|
||||
@@ -178,8 +178,9 @@ static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
i = 0;
|
||||
for_each_child_of_node(np, child) {
|
||||
if (of_node_name_eq(child, "sound")) {
|
||||
of_node_put(sound);
|
||||
i++;
|
||||
sound = child;
|
||||
sound = of_node_get(child);
|
||||
}
|
||||
}
|
||||
if (i == 1) {
|
||||
@@ -205,6 +206,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
}
|
||||
}
|
||||
}
|
||||
of_node_put(sound);
|
||||
/* for the time being, until we can handle non-layout-id
|
||||
* things in some fabric, refuse to attach if there is no
|
||||
* layout-id property or we haven't been forced to attach.
|
||||
@@ -219,7 +221,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
mutex_init(&dev->lock);
|
||||
spin_lock_init(&dev->low_lock);
|
||||
dev->sound.ofdev.archdata.dma_mask = macio->ofdev.archdata.dma_mask;
|
||||
dev->sound.ofdev.dev.of_node = np;
|
||||
dev->sound.ofdev.dev.of_node = of_node_get(np);
|
||||
dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.archdata.dma_mask;
|
||||
dev->sound.ofdev.dev.parent = &macio->ofdev.dev;
|
||||
dev->sound.ofdev.dev.release = i2sbus_release_dev;
|
||||
@@ -327,6 +329,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
for (i=0;i<3;i++)
|
||||
release_and_free_resource(dev->allocated_resource[i]);
|
||||
mutex_destroy(&dev->lock);
|
||||
of_node_put(dev->sound.ofdev.dev.of_node);
|
||||
kfree(dev);
|
||||
return 0;
|
||||
}
|
||||
@@ -405,6 +408,9 @@ static int i2sbus_resume(struct macio_dev* dev)
|
||||
int err, ret = 0;
|
||||
|
||||
list_for_each_entry(i2sdev, &control->list, item) {
|
||||
if (list_empty(&i2sdev->sound.codec_list))
|
||||
continue;
|
||||
|
||||
/* reset i2s bus format etc. */
|
||||
i2sbus_pcm_prepare_both(i2sdev);
|
||||
|
||||
|
||||
@@ -165,17 +165,16 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
|
||||
* currently in use (if any). */
|
||||
hw->rate_min = 5512;
|
||||
hw->rate_max = 192000;
|
||||
/* if the other stream is active, then we can only
|
||||
* support what it is currently using.
|
||||
* FIXME: I lied. This comment is wrong. We can support
|
||||
* anything that works with the same serial format, ie.
|
||||
* when recording 24 bit sound we can well play 16 bit
|
||||
* sound at the same time iff using the same transfer mode.
|
||||
/* If the other stream is already prepared, keep this stream
|
||||
* on the same duplex format and rate.
|
||||
*
|
||||
* i2sbus_pcm_prepare() still programs one shared transport
|
||||
* configuration for both directions, so mixed duplex formats
|
||||
* are not supported here.
|
||||
*/
|
||||
if (other->active) {
|
||||
/* FIXME: is this guaranteed by the alsa api? */
|
||||
hw->formats &= pcm_format_to_bits(i2sdev->format);
|
||||
/* see above, restrict rates to the one we already have */
|
||||
/* Restrict rates to the one already in use. */
|
||||
hw->rate_min = i2sdev->rate;
|
||||
hw->rate_max = i2sdev->rate;
|
||||
}
|
||||
@@ -283,6 +282,23 @@ void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void i2sbus_pcm_clear_active(struct i2sbus_dev *i2sdev, int in)
|
||||
{
|
||||
struct pcm_info *pi;
|
||||
|
||||
guard(mutex)(&i2sdev->lock);
|
||||
|
||||
get_pcm_info(i2sdev, in, &pi, NULL);
|
||||
pi->active = 0;
|
||||
}
|
||||
|
||||
static inline int i2sbus_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, int in)
|
||||
{
|
||||
i2sbus_pcm_clear_active(snd_pcm_substream_chip(substream), in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
|
||||
{
|
||||
struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
|
||||
@@ -291,14 +307,27 @@ static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
|
||||
get_pcm_info(i2sdev, in, &pi, NULL);
|
||||
if (pi->dbdma_ring.stopping)
|
||||
i2sbus_wait_for_stop(i2sdev, pi);
|
||||
i2sbus_pcm_clear_active(i2sdev, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2sbus_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
return i2sbus_hw_params(substream, params, 0);
|
||||
}
|
||||
|
||||
static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return i2sbus_hw_free(substream, 0);
|
||||
}
|
||||
|
||||
static int i2sbus_record_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
return i2sbus_hw_params(substream, params, 1);
|
||||
}
|
||||
|
||||
static int i2sbus_record_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return i2sbus_hw_free(substream, 1);
|
||||
@@ -335,7 +364,6 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
return -EINVAL;
|
||||
|
||||
runtime = pi->substream->runtime;
|
||||
pi->active = 1;
|
||||
if (other->active &&
|
||||
((i2sdev->format != runtime->format)
|
||||
|| (i2sdev->rate != runtime->rate)))
|
||||
@@ -383,6 +411,9 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
/* set stop command */
|
||||
command->command = cpu_to_le16(DBDMA_STOP);
|
||||
|
||||
cii = list_first_entry(&i2sdev->sound.codec_list,
|
||||
struct codec_info_item, list);
|
||||
|
||||
/* ok, let's set the serial format and stuff */
|
||||
switch (runtime->format) {
|
||||
/* 16 bit formats */
|
||||
@@ -390,13 +421,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
case SNDRV_PCM_FORMAT_U16_BE:
|
||||
/* FIXME: if we add different bus factors we need to
|
||||
* do more here!! */
|
||||
bi.bus_factor = 0;
|
||||
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
|
||||
bi.bus_factor = cii->codec->bus_factor;
|
||||
break;
|
||||
}
|
||||
if (!bi.bus_factor)
|
||||
return -ENODEV;
|
||||
bi.bus_factor = cii->codec->bus_factor;
|
||||
input_16bit = 1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_BE:
|
||||
@@ -410,10 +435,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
return -EINVAL;
|
||||
}
|
||||
/* we assume all sysclocks are the same! */
|
||||
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
|
||||
bi.sysclock_factor = cii->codec->sysclock_factor;
|
||||
break;
|
||||
}
|
||||
bi.sysclock_factor = cii->codec->sysclock_factor;
|
||||
|
||||
if (clock_and_divisors(bi.sysclock_factor,
|
||||
bi.bus_factor,
|
||||
@@ -450,9 +472,11 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
|
||||
/* early exit if already programmed correctly */
|
||||
/* not locking these is fine since we touch them only in this function */
|
||||
if (in_le32(&i2sdev->intfregs->serial_format) == sfr
|
||||
&& in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
|
||||
if (in_le32(&i2sdev->intfregs->serial_format) == sfr &&
|
||||
in_le32(&i2sdev->intfregs->data_word_sizes) == dws) {
|
||||
pi->active = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* let's notify the codecs about clocks going away.
|
||||
* For now we only do mastering on the i2s cell... */
|
||||
@@ -490,6 +514,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
|
||||
if (cii->codec->switch_clock)
|
||||
cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
|
||||
|
||||
pi->active = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -734,6 +759,7 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
|
||||
static const struct snd_pcm_ops i2sbus_playback_ops = {
|
||||
.open = i2sbus_playback_open,
|
||||
.close = i2sbus_playback_close,
|
||||
.hw_params = i2sbus_playback_hw_params,
|
||||
.hw_free = i2sbus_playback_hw_free,
|
||||
.prepare = i2sbus_playback_prepare,
|
||||
.trigger = i2sbus_playback_trigger,
|
||||
@@ -802,6 +828,7 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
|
||||
static const struct snd_pcm_ops i2sbus_record_ops = {
|
||||
.open = i2sbus_record_open,
|
||||
.close = i2sbus_record_close,
|
||||
.hw_params = i2sbus_record_hw_params,
|
||||
.hw_free = i2sbus_record_hw_free,
|
||||
.prepare = i2sbus_record_prepare,
|
||||
.trigger = i2sbus_record_trigger,
|
||||
|
||||
@@ -23,6 +23,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
|
||||
# for trace-points
|
||||
CFLAGS_pcm_lib.o := -I$(src)
|
||||
CFLAGS_pcm_native.o := -I$(src)
|
||||
CFLAGS_control.o := -I$(src)
|
||||
|
||||
snd-pcm-dmaengine-y := pcm_dmaengine.o
|
||||
|
||||
|
||||
@@ -41,13 +41,6 @@
|
||||
#define COMPR_CODEC_CAPS_OVERFLOW
|
||||
#endif
|
||||
|
||||
/* TODO:
|
||||
* - add substream support for multiple devices in case of
|
||||
* SND_DYNAMIC_MINORS is not used
|
||||
* - Multiple node representation
|
||||
* driver should be able to register multiple nodes
|
||||
*/
|
||||
|
||||
struct snd_compr_file {
|
||||
unsigned long caps;
|
||||
struct snd_compr_stream stream;
|
||||
@@ -190,9 +183,21 @@ snd_compr_tstamp32_from_64(struct snd_compr_tstamp *tstamp32,
|
||||
static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
|
||||
struct snd_compr_tstamp64 *tstamp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!stream->ops->pointer)
|
||||
return -ENOTSUPP;
|
||||
stream->ops->pointer(stream, tstamp);
|
||||
|
||||
switch (stream->runtime->state) {
|
||||
case SNDRV_PCM_STATE_OPEN:
|
||||
return -EBADFD;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = stream->ops->pointer(stream, tstamp);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset,
|
||||
tstamp->copied_total);
|
||||
if (stream->direction == SND_COMPRESS_PLAYBACK)
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
#include <sound/info.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#ifdef CONFIG_SND_CTL_DEBUG
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "control_trace.h"
|
||||
#else
|
||||
#define trace_snd_ctl_put(card, kctl, iname, expected, actual)
|
||||
#endif
|
||||
|
||||
// Max allocation size for user controls.
|
||||
static int max_user_ctl_alloc_size = 8 * 1024 * 1024;
|
||||
module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444);
|
||||
@@ -1264,6 +1271,72 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
|
||||
return result;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_CTL_DEBUG)
|
||||
|
||||
static const char *const snd_ctl_elem_iface_names[] = {
|
||||
[SNDRV_CTL_ELEM_IFACE_CARD] = "CARD",
|
||||
[SNDRV_CTL_ELEM_IFACE_HWDEP] = "HWDEP",
|
||||
[SNDRV_CTL_ELEM_IFACE_MIXER] = "MIXER",
|
||||
[SNDRV_CTL_ELEM_IFACE_PCM] = "PCM",
|
||||
[SNDRV_CTL_ELEM_IFACE_RAWMIDI] = "RAWMIDI",
|
||||
[SNDRV_CTL_ELEM_IFACE_TIMER] = "TIMER",
|
||||
[SNDRV_CTL_ELEM_IFACE_SEQUENCER] = "SEQUENCER",
|
||||
};
|
||||
|
||||
static int snd_ctl_put_verify(struct snd_card *card, struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *control)
|
||||
{
|
||||
struct snd_ctl_elem_value *original = card->value_buf;
|
||||
struct snd_ctl_elem_info info;
|
||||
const char *iname;
|
||||
int ret, retcmp;
|
||||
|
||||
memset(original, 0, sizeof(*original));
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
ret = kctl->info(kctl, &info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = kctl->get(kctl, original);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = kctl->put(kctl, control);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Sanitize the new value (control->value) before comparing. */
|
||||
fill_remaining_elem_value(control, &info, 0);
|
||||
|
||||
/* With known state for both new and original, do the comparison. */
|
||||
retcmp = memcmp(&original->value, &control->value, sizeof(original->value));
|
||||
if (retcmp)
|
||||
retcmp = 1;
|
||||
|
||||
iname = snd_ctl_elem_iface_names[kctl->id.iface];
|
||||
trace_snd_ctl_put(&kctl->id, iname, card->number, ret, retcmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *control, unsigned int access)
|
||||
{
|
||||
if ((access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK) ||
|
||||
(access & SNDRV_CTL_ELEM_ACCESS_VOLATILE))
|
||||
return kctl->put(kctl, control);
|
||||
|
||||
return snd_ctl_put_verify(card, kctl, control);
|
||||
}
|
||||
#else
|
||||
static inline int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *control, unsigned int access)
|
||||
{
|
||||
return kctl->put(kctl, control);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
|
||||
struct snd_ctl_elem_value *control)
|
||||
{
|
||||
@@ -1300,7 +1373,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
|
||||
false);
|
||||
}
|
||||
if (!result)
|
||||
result = kctl->put(kctl, control);
|
||||
result = snd_ctl_put(card, kctl, control, vd->access);
|
||||
|
||||
if (result < 0) {
|
||||
up_write(&card->controls_rwsem);
|
||||
return result;
|
||||
|
||||
55
sound/core/control_trace.h
Normal file
55
sound/core/control_trace.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM snd_ctl
|
||||
|
||||
#if !defined(_TRACE_SND_CTL_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_SND_CTL_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <uapi/sound/asound.h>
|
||||
|
||||
TRACE_EVENT(snd_ctl_put,
|
||||
|
||||
TP_PROTO(struct snd_ctl_elem_id *id, const char *iname, unsigned int card,
|
||||
int expected, int actual),
|
||||
|
||||
TP_ARGS(id, iname, card, expected, actual),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, numid)
|
||||
__string(iname, iname)
|
||||
__string(kname, id->name)
|
||||
__field(unsigned int, index)
|
||||
__field(unsigned int, device)
|
||||
__field(unsigned int, subdevice)
|
||||
__field(unsigned int, card)
|
||||
__field(int, expected)
|
||||
__field(int, actual)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->numid = id->numid;
|
||||
__assign_str(iname);
|
||||
__assign_str(kname);
|
||||
__entry->index = id->index;
|
||||
__entry->device = id->device;
|
||||
__entry->subdevice = id->subdevice;
|
||||
__entry->card = card;
|
||||
__entry->expected = expected;
|
||||
__entry->actual = actual;
|
||||
),
|
||||
|
||||
TP_printk("%s: expected=%d, actual=%d for ctl numid=%d, iface=%s, name='%s', index=%d, device=%d, subdevice=%d, card=%d\n",
|
||||
__entry->expected == __entry->actual ? "success" : "fail",
|
||||
__entry->expected, __entry->actual, __entry->numid,
|
||||
__get_str(iname), __get_str(kname), __entry->index,
|
||||
__entry->device, __entry->subdevice, __entry->card)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_SND_CTL_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#define TRACE_INCLUDE_FILE control_trace
|
||||
#include <trace/define_trace.h>
|
||||
@@ -362,6 +362,11 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev),
|
||||
sound_debugfs_root);
|
||||
#endif
|
||||
#ifdef CONFIG_SND_CTL_DEBUG
|
||||
card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL);
|
||||
if (!card->value_buf)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@@ -587,6 +592,9 @@ static int snd_card_do_free(struct snd_card *card)
|
||||
snd_device_free_all(card);
|
||||
if (card->private_free)
|
||||
card->private_free(card);
|
||||
#ifdef CONFIG_SND_CTL_DEBUG
|
||||
kfree(card->value_buf);
|
||||
#endif
|
||||
if (snd_info_card_free(card) < 0) {
|
||||
dev_warn(card->dev, "unable to free card info\n");
|
||||
/* Not fatal error */
|
||||
|
||||
@@ -1227,14 +1227,16 @@ static int snd_pcm_oss_capture_position_fixup(struct snd_pcm_substream *substrea
|
||||
snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_state_t state;
|
||||
int ret;
|
||||
while (1) {
|
||||
if (runtime->state == SNDRV_PCM_STATE_XRUN ||
|
||||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
state = snd_pcm_get_state(substream);
|
||||
if (state == SNDRV_PCM_STATE_XRUN ||
|
||||
state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
#ifdef OSS_DEBUG
|
||||
pcm_dbg(substream->pcm,
|
||||
"pcm_oss: write: recovering from %s\n",
|
||||
runtime->state == SNDRV_PCM_STATE_XRUN ?
|
||||
state == SNDRV_PCM_STATE_XRUN ?
|
||||
"XRUN" : "SUSPEND");
|
||||
#endif
|
||||
ret = snd_pcm_oss_prepare(substream);
|
||||
@@ -1249,7 +1251,7 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
|
||||
break;
|
||||
/* test, if we can't store new data, because the stream */
|
||||
/* has not been started */
|
||||
if (runtime->state == SNDRV_PCM_STATE_PREPARED)
|
||||
if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED)
|
||||
return -EAGAIN;
|
||||
}
|
||||
return ret;
|
||||
@@ -1259,20 +1261,22 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_sframes_t delay;
|
||||
snd_pcm_state_t state;
|
||||
int ret;
|
||||
while (1) {
|
||||
if (runtime->state == SNDRV_PCM_STATE_XRUN ||
|
||||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
state = snd_pcm_get_state(substream);
|
||||
if (state == SNDRV_PCM_STATE_XRUN ||
|
||||
state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
#ifdef OSS_DEBUG
|
||||
pcm_dbg(substream->pcm,
|
||||
"pcm_oss: read: recovering from %s\n",
|
||||
runtime->state == SNDRV_PCM_STATE_XRUN ?
|
||||
state == SNDRV_PCM_STATE_XRUN ?
|
||||
"XRUN" : "SUSPEND");
|
||||
#endif
|
||||
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
} else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
|
||||
} else if (state == SNDRV_PCM_STATE_SETUP) {
|
||||
ret = snd_pcm_oss_prepare(substream);
|
||||
if (ret < 0)
|
||||
break;
|
||||
@@ -1285,7 +1289,7 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
|
||||
frames, in_kernel);
|
||||
mutex_lock(&runtime->oss.params_lock);
|
||||
if (ret == -EPIPE) {
|
||||
if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_DRAINING) {
|
||||
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
@@ -1301,15 +1305,16 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
|
||||
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
|
||||
snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_state_t state;
|
||||
int ret;
|
||||
while (1) {
|
||||
if (runtime->state == SNDRV_PCM_STATE_XRUN ||
|
||||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
state = snd_pcm_get_state(substream);
|
||||
if (state == SNDRV_PCM_STATE_XRUN ||
|
||||
state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
#ifdef OSS_DEBUG
|
||||
pcm_dbg(substream->pcm,
|
||||
"pcm_oss: writev: recovering from %s\n",
|
||||
runtime->state == SNDRV_PCM_STATE_XRUN ?
|
||||
state == SNDRV_PCM_STATE_XRUN ?
|
||||
"XRUN" : "SUSPEND");
|
||||
#endif
|
||||
ret = snd_pcm_oss_prepare(substream);
|
||||
@@ -1322,7 +1327,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void
|
||||
|
||||
/* test, if we can't store new data, because the stream */
|
||||
/* has not been started */
|
||||
if (runtime->state == SNDRV_PCM_STATE_PREPARED)
|
||||
if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED)
|
||||
return -EAGAIN;
|
||||
}
|
||||
return ret;
|
||||
@@ -1330,21 +1335,22 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_state_t state;
|
||||
int ret;
|
||||
while (1) {
|
||||
if (runtime->state == SNDRV_PCM_STATE_XRUN ||
|
||||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
state = snd_pcm_get_state(substream);
|
||||
if (state == SNDRV_PCM_STATE_XRUN ||
|
||||
state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
#ifdef OSS_DEBUG
|
||||
pcm_dbg(substream->pcm,
|
||||
"pcm_oss: readv: recovering from %s\n",
|
||||
runtime->state == SNDRV_PCM_STATE_XRUN ?
|
||||
state == SNDRV_PCM_STATE_XRUN ?
|
||||
"XRUN" : "SUSPEND");
|
||||
#endif
|
||||
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
} else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
|
||||
} else if (state == SNDRV_PCM_STATE_SETUP) {
|
||||
ret = snd_pcm_oss_prepare(substream);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
@@ -430,11 +430,13 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
|
||||
if (!boundary)
|
||||
boundary = 0x7fffffff;
|
||||
scoped_guard(pcm_stream_lock_irq, substream) {
|
||||
/* FIXME: we should consider the boundary for the sync from app */
|
||||
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
|
||||
control->appl_ptr = scontrol.appl_ptr;
|
||||
else
|
||||
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
|
||||
err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
scontrol.appl_ptr = control->appl_ptr % boundary;
|
||||
}
|
||||
if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
|
||||
control->avail_min = scontrol.avail_min;
|
||||
else
|
||||
|
||||
@@ -618,13 +618,32 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
|
||||
return usecs;
|
||||
}
|
||||
|
||||
static void snd_pcm_set_state(struct snd_pcm_substream *substream,
|
||||
snd_pcm_state_t state)
|
||||
/**
|
||||
* snd_pcm_set_state - Set the PCM runtime state with stream lock
|
||||
* @substream: PCM substream
|
||||
* @state: state to set
|
||||
*/
|
||||
void snd_pcm_set_state(struct snd_pcm_substream *substream,
|
||||
snd_pcm_state_t state)
|
||||
{
|
||||
guard(pcm_stream_lock_irq)(substream);
|
||||
if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED)
|
||||
__snd_pcm_set_state(substream->runtime, state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_set_state);
|
||||
|
||||
/**
|
||||
* snd_pcm_get_state - Read the PCM runtime state with stream lock
|
||||
* @substream: PCM substream
|
||||
*
|
||||
* Return: the current PCM state
|
||||
*/
|
||||
snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream)
|
||||
{
|
||||
guard(pcm_stream_lock_irqsave)(substream);
|
||||
return substream->runtime->state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_get_state);
|
||||
|
||||
static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
|
||||
int event)
|
||||
@@ -1761,6 +1780,9 @@ static int snd_pcm_suspend(struct snd_pcm_substream *substream)
|
||||
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
|
||||
* @pcm: the PCM instance
|
||||
*
|
||||
* Takes and releases pcm->open_mutex to serialize against
|
||||
* concurrent open/close while walking the substreams.
|
||||
*
|
||||
* After this call, all streams are changed to SUSPENDED state.
|
||||
*
|
||||
* Return: Zero if successful (or @pcm is %NULL), or a negative error code.
|
||||
@@ -1773,8 +1795,9 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm)
|
||||
if (! pcm)
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&pcm->open_mutex);
|
||||
|
||||
for_each_pcm_substream(pcm, stream, substream) {
|
||||
/* FIXME: the open/close code should lock this as well */
|
||||
if (!substream->runtime)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -101,9 +101,9 @@ snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count,
|
||||
break;
|
||||
}
|
||||
fmt = (*(unsigned short *)rec.c) & 0xffff;
|
||||
/* FIXME the return value isn't correct */
|
||||
return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
|
||||
fmt, buf, 0, count);
|
||||
err = snd_seq_oss_synth_load_patch(dp, rec.s.dev,
|
||||
fmt, buf, 0, count);
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
if (ev_is_long(&rec)) {
|
||||
/* extended code */
|
||||
|
||||
@@ -841,7 +841,7 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
|
||||
unsigned char index = event->data.control.param & 0x7f;
|
||||
unsigned char val = event->data.control.value & 0x7f;
|
||||
struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* process special CC's (bank/rpn/nrpn) */
|
||||
switch (index) {
|
||||
@@ -851,47 +851,54 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
|
||||
cc->cc_rpn_msb = val;
|
||||
if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
|
||||
reset_rpn(cc);
|
||||
return ret;
|
||||
break;
|
||||
case UMP_CC_RPN_LSB:
|
||||
ret = fill_rpn(cc, data, channel, true);
|
||||
cc->rpn_set = 1;
|
||||
cc->cc_rpn_lsb = val;
|
||||
if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
|
||||
reset_rpn(cc);
|
||||
return ret;
|
||||
break;
|
||||
case UMP_CC_NRPN_MSB:
|
||||
ret = fill_rpn(cc, data, channel, true);
|
||||
cc->nrpn_set = 1;
|
||||
cc->cc_nrpn_msb = val;
|
||||
return ret;
|
||||
break;
|
||||
case UMP_CC_NRPN_LSB:
|
||||
ret = fill_rpn(cc, data, channel, true);
|
||||
cc->nrpn_set = 1;
|
||||
cc->cc_nrpn_lsb = val;
|
||||
return ret;
|
||||
break;
|
||||
case UMP_CC_DATA:
|
||||
cc->cc_data_msb_set = 1;
|
||||
cc->cc_data_msb = val;
|
||||
return fill_rpn(cc, data, channel, false);
|
||||
ret = fill_rpn(cc, data, channel, false);
|
||||
break;
|
||||
case UMP_CC_BANK_SELECT:
|
||||
cc->bank_set = 1;
|
||||
cc->cc_bank_msb = val;
|
||||
return 0; // skip
|
||||
ret = 0; // skip
|
||||
break;
|
||||
case UMP_CC_BANK_SELECT_LSB:
|
||||
cc->bank_set = 1;
|
||||
cc->cc_bank_lsb = val;
|
||||
return 0; // skip
|
||||
ret = 0; // skip
|
||||
break;
|
||||
case UMP_CC_DATA_LSB:
|
||||
cc->cc_data_lsb_set = 1;
|
||||
cc->cc_data_lsb = val;
|
||||
return fill_rpn(cc, data, channel, false);
|
||||
ret = fill_rpn(cc, data, channel, false);
|
||||
break;
|
||||
default:
|
||||
data->cc.status = status;
|
||||
data->cc.channel = channel;
|
||||
data->cc.index = index;
|
||||
data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
data->cc.status = status;
|
||||
data->cc.channel = channel;
|
||||
data->cc.index = index;
|
||||
data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* convert one-parameter control event to MIDI 2.0 UMP */
|
||||
|
||||
@@ -216,9 +216,16 @@ static int snd_find_free_minor(int type, struct snd_card *card, int dev)
|
||||
case SNDRV_DEVICE_TYPE_RAWMIDI:
|
||||
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
|
||||
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
|
||||
if (snd_BUG_ON(!card))
|
||||
return -EINVAL;
|
||||
minor = SNDRV_MINOR(card->number, type + dev);
|
||||
break;
|
||||
case SNDRV_DEVICE_TYPE_COMPRESS:
|
||||
if (snd_BUG_ON(!card))
|
||||
return -EINVAL;
|
||||
if (dev < 0 ||
|
||||
dev >= SNDRV_MINOR_HWDEP - SNDRV_MINOR_COMPRESS)
|
||||
return -EINVAL;
|
||||
minor = SNDRV_MINOR(card->number, type + dev);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -129,6 +129,9 @@ static LIST_HEAD(snd_timer_list);
|
||||
/* list of slave instances */
|
||||
static LIST_HEAD(snd_timer_slave_list);
|
||||
|
||||
/* list of open master instances that can accept slave links */
|
||||
static LIST_HEAD(snd_timer_master_list);
|
||||
|
||||
/* lock for slave active lists */
|
||||
static DEFINE_SPINLOCK(slave_active_lock);
|
||||
|
||||
@@ -161,6 +164,7 @@ struct snd_timer_instance *snd_timer_instance_new(const char *owner)
|
||||
}
|
||||
INIT_LIST_HEAD(&timeri->open_list);
|
||||
INIT_LIST_HEAD(&timeri->active_list);
|
||||
INIT_LIST_HEAD(&timeri->master_list);
|
||||
INIT_LIST_HEAD(&timeri->ack_list);
|
||||
INIT_LIST_HEAD(&timeri->slave_list_head);
|
||||
INIT_LIST_HEAD(&timeri->slave_active_head);
|
||||
@@ -245,6 +249,12 @@ static int check_matching_master_slave(struct snd_timer_instance *master,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool snd_timer_has_slave_key(const struct snd_timer_instance *timeri)
|
||||
{
|
||||
return !(timeri->flags & SNDRV_TIMER_IFLG_SLAVE) &&
|
||||
timeri->slave_class > SNDRV_TIMER_SCLASS_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* look for a master instance matching with the slave id of the given slave.
|
||||
* when found, relink the open_link of the slave.
|
||||
@@ -253,19 +263,15 @@ static int check_matching_master_slave(struct snd_timer_instance *master,
|
||||
*/
|
||||
static int snd_timer_check_slave(struct snd_timer_instance *slave)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
struct snd_timer_instance *master;
|
||||
int err = 0;
|
||||
|
||||
/* FIXME: it's really dumb to look up all entries.. */
|
||||
list_for_each_entry(timer, &snd_timer_list, device_list) {
|
||||
list_for_each_entry(master, &timer->open_list_head, open_list) {
|
||||
err = check_matching_master_slave(master, slave);
|
||||
if (err != 0) /* match found or error */
|
||||
goto out;
|
||||
}
|
||||
list_for_each_entry(master, &snd_timer_master_list, master_list) {
|
||||
err = check_matching_master_slave(master, slave);
|
||||
if (err != 0) /* match found or error */
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
out:
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
@@ -377,6 +383,8 @@ int snd_timer_open(struct snd_timer_instance *timeri,
|
||||
timeri->slave_id = slave_id;
|
||||
|
||||
list_add_tail(&timeri->open_list, &timer->open_list_head);
|
||||
if (snd_timer_has_slave_key(timeri))
|
||||
list_add_tail(&timeri->master_list, &snd_timer_master_list);
|
||||
timer->num_instances++;
|
||||
err = snd_timer_check_master(timeri);
|
||||
list_added:
|
||||
@@ -431,6 +439,9 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri,
|
||||
num_slaves--;
|
||||
}
|
||||
|
||||
if (!list_empty(&timeri->master_list))
|
||||
list_del_init(&timeri->master_list);
|
||||
|
||||
/* force to stop the timer */
|
||||
snd_timer_stop(timeri);
|
||||
|
||||
|
||||
@@ -151,10 +151,13 @@ efw_transaction(struct snd_efw *efw, unsigned int category,
|
||||
(be32_to_cpu(header->category) != category) ||
|
||||
(be32_to_cpu(header->command) != command) ||
|
||||
(be32_to_cpu(header->status) != EFR_STATUS_OK)) {
|
||||
u32 st = be32_to_cpu(header->status);
|
||||
|
||||
dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
|
||||
be32_to_cpu(header->category),
|
||||
be32_to_cpu(header->command),
|
||||
efr_status_names[be32_to_cpu(header->status)]);
|
||||
st < ARRAY_SIZE(efr_status_names) ?
|
||||
efr_status_names[st] : "unknown");
|
||||
err = -EIO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ struct ad198x_spec {
|
||||
|
||||
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
|
||||
int num_smux_conns;
|
||||
|
||||
unsigned int gpio_data;
|
||||
};
|
||||
|
||||
|
||||
@@ -934,9 +936,9 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
|
||||
|
||||
if (spec->eapd_nid)
|
||||
ad_vmaster_eapd_hook(private_data, enabled);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA,
|
||||
enabled ? 0x00 : 0x02);
|
||||
spec->gpio_data = enabled ? 0x00 : 0x02;
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, spec->gpio_data);
|
||||
}
|
||||
|
||||
static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
|
||||
@@ -948,12 +950,7 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
|
||||
spec->gen.own_eapd_ctl = 1;
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_MASK, 0x02);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, 0x02);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x02);
|
||||
spec->gpio_data = 0x02;
|
||||
break;
|
||||
case HDA_FIXUP_ACT_PROBE:
|
||||
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
|
||||
@@ -961,6 +958,9 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
|
||||
else
|
||||
spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
|
||||
break;
|
||||
case HDA_FIXUP_ACT_INIT:
|
||||
snd_hda_codec_set_gpio(codec, 0x02, 0x02, spec->gpio_data, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3755,22 +3755,12 @@ static void ca0132_gpio_setup(struct hda_codec *codec)
|
||||
|
||||
switch (ca0132_quirk(spec)) {
|
||||
case QUIRK_SBZ:
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, 0x07);
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_MASK, 0x07);
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x04);
|
||||
snd_hda_codec_set_gpio(codec, 0x07, 0x07, 0x04, 0);
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x06);
|
||||
break;
|
||||
case QUIRK_R3DI:
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, 0x1E);
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_MASK, 0x1F);
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x0C);
|
||||
snd_hda_codec_set_gpio(codec, 0x1F, 0x1E, 0x0C, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -264,14 +264,9 @@ static int cs_init(struct hda_codec *codec)
|
||||
|
||||
snd_hda_gen_init(codec);
|
||||
|
||||
if (spec->gpio_mask) {
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
|
||||
spec->gpio_mask);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
spec->gpio_dir);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
if (spec->gpio_mask)
|
||||
snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir,
|
||||
spec->gpio_data, 0);
|
||||
|
||||
if (spec->vendor_nid == CS420X_VENDOR_NID) {
|
||||
init_input_coef(codec);
|
||||
|
||||
@@ -442,14 +442,9 @@ static int cs421x_init(struct hda_codec *codec)
|
||||
|
||||
snd_hda_gen_init(codec);
|
||||
|
||||
if (spec->gpio_mask) {
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
|
||||
spec->gpio_mask);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
spec->gpio_dir);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
if (spec->gpio_mask)
|
||||
snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir,
|
||||
spec->gpio_data, 0);
|
||||
|
||||
cs4210_spdif_automute(codec, NULL);
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_para
|
||||
return 0;
|
||||
|
||||
error:
|
||||
codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
|
||||
codec_err(codec, "I2C Bulk Read Failed 0x%02x\n", scodec->addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -1042,14 +1042,9 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
|
||||
struct cs8409_spec *spec = codec->spec;
|
||||
struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
|
||||
|
||||
if (spec->gpio_mask) {
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
|
||||
spec->gpio_mask);
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
spec->gpio_dir);
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
if (spec->gpio_mask)
|
||||
snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir,
|
||||
spec->gpio_data, 0);
|
||||
|
||||
for (; seq->nid; seq++)
|
||||
cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
|
||||
@@ -1442,14 +1437,9 @@ static void dolphin_hw_init(struct hda_codec *codec)
|
||||
struct sub_codec *cs42l42;
|
||||
int i;
|
||||
|
||||
if (spec->gpio_mask) {
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
|
||||
spec->gpio_mask);
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
spec->gpio_dir);
|
||||
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
if (spec->gpio_mask)
|
||||
snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir,
|
||||
spec->gpio_data, 0);
|
||||
|
||||
for (; seq->nid; seq++)
|
||||
cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
|
||||
|
||||
@@ -39,13 +39,6 @@ static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id)
|
||||
spec->out_vol_mask = (1ULL << 0x10);
|
||||
}
|
||||
|
||||
err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_hda_gen_parse_auto_config(codec, cfg);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@@ -154,14 +154,8 @@ static void cxt_init_gpio_led(struct hda_codec *codec)
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
|
||||
|
||||
if (mask) {
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
|
||||
mask);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
mask);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_led);
|
||||
}
|
||||
if (mask)
|
||||
snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0);
|
||||
}
|
||||
|
||||
static void cx_fixup_headset_recog(struct hda_codec *codec)
|
||||
@@ -775,9 +769,7 @@ static void cxt_setup_gpio_unmute(struct hda_codec *codec,
|
||||
{
|
||||
if (gpio_mute_mask) {
|
||||
// set gpio data to 0.
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask);
|
||||
snd_hda_codec_set_gpio(codec, gpio_mute_mask, gpio_mute_mask, 0, 0);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,7 +863,7 @@ static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
if (nid) {
|
||||
msleep(10);
|
||||
snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
|
||||
snd_hda_codec_write_sync(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
}
|
||||
}
|
||||
|
||||
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
|
||||
snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
|
||||
snd_hda_codec_set_power_to_all(codec, fg, power_state);
|
||||
}
|
||||
|
||||
|
||||
@@ -1004,13 +1004,6 @@ static int alc269_resume(struct hda_codec *codec)
|
||||
snd_hda_regmap_sync(codec);
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
|
||||
/* on some machine, the BIOS will clear the codec gpio data when enter
|
||||
* suspend, and won't restore the data after resume, so we restore it
|
||||
* in the driver.
|
||||
*/
|
||||
if (spec->gpio_data)
|
||||
alc_write_gpio_data(codec);
|
||||
|
||||
if (spec->has_alc5505_dsp)
|
||||
alc5505_dsp_resume(codec);
|
||||
|
||||
@@ -2296,9 +2289,9 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
|
||||
alc255_set_default_jack_type(codec);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
alc_fixup_headset_mode(codec, fix, action);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc288_update_headset_jack_cb(struct hda_codec *codec,
|
||||
@@ -6639,10 +6632,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc288_fixup_surface_swap_dacs,
|
||||
},
|
||||
[ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc233_fixup_lenovo_gpio2_mic_hotkey,
|
||||
},
|
||||
[ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc233_fixup_lenovo_gpio2_mic_hotkey,
|
||||
},
|
||||
[ALC245_FIXUP_BASS_HP_DAC] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
/* Borrow the DAC routing selected for those Thinkpads */
|
||||
@@ -7198,6 +7191,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e75, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e9c, "HP 16 Clipper OmniBook X X360", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
@@ -7219,8 +7213,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ee7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f07, "HP Agusta G7KX", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f2d, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f2e, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
|
||||
@@ -7365,6 +7362,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3601, "ASUS PM5406CGA", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3611, "ASUS PM5606CGA", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3701, "ASUS P5406CCA", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3711, "ASUS P5606CCA", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
|
||||
SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
|
||||
SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
|
||||
|
||||
@@ -255,6 +255,17 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
|
||||
alc_fixup_headset_mode(codec, fix, action);
|
||||
}
|
||||
|
||||
static void alc662_fixup_csl_amp(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
if (action == HDA_FIXUP_ACT_INIT) {
|
||||
/* need to toggle GPIO to enable the amp */
|
||||
snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0);
|
||||
msleep(100);
|
||||
snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
ALC662_FIXUP_ASPIRE,
|
||||
ALC662_FIXUP_LED_GPIO1,
|
||||
@@ -313,6 +324,7 @@ enum {
|
||||
ALC897_FIXUP_HEADSET_MIC_PIN2,
|
||||
ALC897_FIXUP_UNIS_H3C_X500S,
|
||||
ALC897_FIXUP_HEADSET_MIC_PIN3,
|
||||
ALC662_FIXUP_CSL_GPIO,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc662_fixups[] = {
|
||||
@@ -766,11 +778,16 @@ static const struct hda_fixup alc662_fixups[] = {
|
||||
{ }
|
||||
},
|
||||
},
|
||||
[ALC662_FIXUP_CSL_GPIO] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc662_fixup_csl_amp,
|
||||
},
|
||||
};
|
||||
|
||||
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(0x1022, 0xc950, "CSL Unity BF24B", ALC662_FIXUP_CSL_GPIO),
|
||||
SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
|
||||
|
||||
@@ -99,15 +99,6 @@ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK");
|
||||
|
||||
void alc_write_gpio_data(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK");
|
||||
|
||||
void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
|
||||
bool on)
|
||||
{
|
||||
@@ -119,26 +110,22 @@ void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
|
||||
else
|
||||
spec->gpio_data &= ~mask;
|
||||
if (oldval != spec->gpio_data)
|
||||
alc_write_gpio_data(codec);
|
||||
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_data);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK");
|
||||
|
||||
void alc_write_gpio(struct hda_codec *codec)
|
||||
static void alc_write_gpio(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (!spec->gpio_mask)
|
||||
return;
|
||||
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir);
|
||||
if (spec->gpio_write_delay)
|
||||
msleep(1);
|
||||
alc_write_gpio_data(codec);
|
||||
snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir,
|
||||
spec->gpio_data,
|
||||
spec->gpio_write_delay ? 1 : 0);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK");
|
||||
|
||||
void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask)
|
||||
{
|
||||
@@ -411,9 +398,8 @@ void alc_headset_mic_no_shutup(struct hda_codec *codec)
|
||||
return;
|
||||
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
/* use read here for syncing after issuing each verb */
|
||||
if (pin->nid != mic_pin)
|
||||
snd_hda_codec_read(codec, pin->nid, 0,
|
||||
snd_hda_codec_write_sync(codec, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
}
|
||||
|
||||
@@ -2164,8 +2150,7 @@ void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
|
||||
}
|
||||
else
|
||||
} else
|
||||
alc_fixup_headset_mode(codec, fix, action);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK");
|
||||
|
||||
@@ -168,10 +168,8 @@ void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw);
|
||||
* GPIO helpers
|
||||
*/
|
||||
void alc_setup_gpio(struct hda_codec *codec, unsigned int mask);
|
||||
void alc_write_gpio_data(struct hda_codec *codec);
|
||||
void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
|
||||
bool on);
|
||||
void alc_write_gpio(struct hda_codec *codec);
|
||||
|
||||
/* common GPIO fixups */
|
||||
void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask);
|
||||
|
||||
@@ -36,6 +36,32 @@ struct senary_spec {
|
||||
unsigned int gpio_mic_led_mask;
|
||||
};
|
||||
|
||||
enum {
|
||||
SENARY_FIXUP_PINCFG_DEFAULT,
|
||||
};
|
||||
|
||||
static const struct hda_pintbl senary_pincfg_default[] = {
|
||||
{ 0x16, 0x02211020 }, /* Headphone */
|
||||
{ 0x17, 0x40f001f0 }, /* Not used */
|
||||
{ 0x18, 0x05a1904d }, /* Mic */
|
||||
{ 0x19, 0x02a1104e }, /* Headset Mic */
|
||||
{ 0x1a, 0x01819030 }, /* Line-in */
|
||||
{ 0x1d, 0x01014010 }, /* Line-out */
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_fixup senary_fixups[] = {
|
||||
[SENARY_FIXUP_PINCFG_DEFAULT] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = senary_pincfg_default,
|
||||
},
|
||||
};
|
||||
|
||||
/* Quirk table for specific machines can be added here */
|
||||
static const struct hda_quirk sn6186_fixups[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
/* additional beep mixers; private_value will be overwritten */
|
||||
static const struct snd_kcontrol_new senary_beep_mixer[] = {
|
||||
@@ -50,7 +76,6 @@ static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
|
||||
unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
|
||||
int i;
|
||||
|
||||
spec->gen.beep_nid = nid;
|
||||
for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) {
|
||||
knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
|
||||
&senary_beep_mixer[i]);
|
||||
@@ -58,6 +83,8 @@ static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
|
||||
return -ENOMEM;
|
||||
knew->private_value = beep_amp;
|
||||
}
|
||||
|
||||
spec->gen.beep_nid = nid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -93,16 +120,28 @@ static void senary_auto_parse_eapd(struct hda_codec *codec)
|
||||
}
|
||||
}
|
||||
|
||||
/* Hardware specific initialization verbs */
|
||||
static void senary_init_verb(struct hda_codec *codec)
|
||||
{
|
||||
/* Vendor specific init sequence */
|
||||
snd_hda_codec_write(codec, 0x1b, 0x0, 0x05a, 0xaa);
|
||||
snd_hda_codec_write(codec, 0x1b, 0x0, 0x059, 0x48);
|
||||
snd_hda_codec_write(codec, 0x1b, 0x0, 0x01b, 0x00);
|
||||
snd_hda_codec_write(codec, 0x1b, 0x0, 0x01c, 0x00);
|
||||
|
||||
/* Override pin caps for headset mic */
|
||||
snd_hda_override_pin_caps(codec, 0x19, 0x2124);
|
||||
}
|
||||
|
||||
static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins,
|
||||
const hda_nid_t *pins, bool on)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pins; i++) {
|
||||
if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
|
||||
snd_hda_codec_write(codec, pins[i], 0,
|
||||
AC_VERB_SET_EAPD_BTLENABLE,
|
||||
on ? 0x02 : 0);
|
||||
snd_hda_codec_write(codec, pins[i], 0,
|
||||
AC_VERB_SET_EAPD_BTLENABLE,
|
||||
on ? 0x02 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,14 +159,8 @@ static void senary_init_gpio_led(struct hda_codec *codec)
|
||||
struct senary_spec *spec = codec->spec;
|
||||
unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
|
||||
|
||||
if (mask) {
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK,
|
||||
mask);
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION,
|
||||
mask);
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA,
|
||||
spec->gpio_led);
|
||||
}
|
||||
if (mask)
|
||||
snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0);
|
||||
}
|
||||
|
||||
static int senary_init(struct hda_codec *codec)
|
||||
@@ -136,6 +169,7 @@ static int senary_init(struct hda_codec *codec)
|
||||
|
||||
snd_hda_gen_init(codec);
|
||||
senary_init_gpio_led(codec);
|
||||
senary_init_verb(codec);
|
||||
if (!spec->dynamic_eapd)
|
||||
senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
|
||||
@@ -181,11 +215,30 @@ static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id)
|
||||
senary_auto_parse_eapd(codec);
|
||||
spec->gen.own_eapd_ctl = 1;
|
||||
|
||||
if (!spec->gen.vmaster_mute.hook)
|
||||
spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
|
||||
/* Setup fixups based on codec vendor ID */
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x1fa86186:
|
||||
codec->pin_amp_workaround = 1;
|
||||
spec->gen.mixer_nid = 0x15;
|
||||
snd_hda_pick_fixup(codec, NULL, sn6186_fixups, senary_fixups);
|
||||
|
||||
/* If no specific quirk found, apply the default pin configuration */
|
||||
if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET)
|
||||
codec->fixup_id = SENARY_FIXUP_PINCFG_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
snd_hda_pick_fixup(codec, NULL, sn6186_fixups, senary_fixups);
|
||||
break;
|
||||
}
|
||||
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
|
||||
|
||||
/* Run hardware init verbs once during probe */
|
||||
senary_init_verb(codec);
|
||||
|
||||
if (!spec->gen.vmaster_mute.hook)
|
||||
spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
|
||||
|
||||
err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
|
||||
spec->parse_flags);
|
||||
if (err < 0)
|
||||
|
||||
@@ -55,6 +55,11 @@ static const struct cs35l41_config cs35l41_config_table[] = {
|
||||
{ "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
|
||||
{ "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
|
||||
{ "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 },
|
||||
/*
|
||||
* Device 103C8B63 has _DSD with valid reset-gpios and cs-gpios, however the
|
||||
* boost type is incorrectly set to Internal. Override to External Boost.
|
||||
*/
|
||||
{ "103C8B63", 4, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, -1, -1, -1, 0, 0, 0 },
|
||||
{ "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
|
||||
{ "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
|
||||
{ "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
|
||||
@@ -475,6 +480,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
|
||||
{ "CSC3551", "103C8A30", generic_dsd_config },
|
||||
{ "CSC3551", "103C8A31", generic_dsd_config },
|
||||
{ "CSC3551", "103C8A6E", generic_dsd_config },
|
||||
{ "CSC3551", "103C8B63", generic_dsd_config },
|
||||
{ "CSC3551", "103C8BB3", generic_dsd_config },
|
||||
{ "CSC3551", "103C8BB4", generic_dsd_config },
|
||||
{ "CSC3551", "103C8BDD", generic_dsd_config },
|
||||
|
||||
@@ -309,15 +309,7 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
|
||||
/* Configure GPIOx as CMOS */
|
||||
snd_hda_codec_write(codec, fg, 0, 0x7e7, 0);
|
||||
|
||||
snd_hda_codec_write(codec, fg, 0,
|
||||
AC_VERB_SET_GPIO_MASK, gpiomask);
|
||||
snd_hda_codec_read(codec, fg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
|
||||
|
||||
msleep(1);
|
||||
|
||||
snd_hda_codec_read(codec, fg, 0,
|
||||
AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
|
||||
snd_hda_codec_set_gpio(codec, gpiomask, gpiodir, gpiostate, 1);
|
||||
}
|
||||
|
||||
/* hook for controlling mic-mute LED GPIO */
|
||||
|
||||
@@ -606,9 +606,8 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
|
||||
if (codec->bus->shutdown)
|
||||
return;
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
/* use read here for syncing after issuing each verb */
|
||||
snd_hda_codec_read(codec, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
snd_hda_codec_write_sync(codec, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
}
|
||||
codec->pins_shutup = 1;
|
||||
}
|
||||
@@ -2529,7 +2528,10 @@ EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
|
||||
static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_multi_out *mout = (void *)kcontrol->private_value;
|
||||
|
||||
guard(mutex)(&codec->spdif_mutex);
|
||||
ucontrol->value.integer.value[0] = mout->share_spdif;
|
||||
return 0;
|
||||
}
|
||||
@@ -2537,9 +2539,15 @@ static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
|
||||
static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
|
||||
mout->share_spdif = !!ucontrol->value.integer.value[0];
|
||||
return 0;
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_multi_out *mout = (void *)kcontrol->private_value;
|
||||
bool val = !!ucontrol->value.integer.value[0];
|
||||
int change;
|
||||
|
||||
guard(mutex)(&codec->spdif_mutex);
|
||||
change = mout->share_spdif != val;
|
||||
mout->share_spdif = val;
|
||||
return change;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new spdif_share_sw = {
|
||||
@@ -2550,6 +2558,14 @@ static const struct snd_kcontrol_new spdif_share_sw = {
|
||||
.put = spdif_share_sw_put,
|
||||
};
|
||||
|
||||
static void notify_spdif_share_sw(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout)
|
||||
{
|
||||
if (mout->share_spdif_kctl)
|
||||
snd_ctl_notify_one(codec->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
mout->share_spdif_kctl, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_create_spdif_share_sw - create Default PCM switch
|
||||
* @codec: the HDA codec
|
||||
@@ -2559,15 +2575,24 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout)
|
||||
{
|
||||
struct snd_kcontrol *kctl;
|
||||
int err;
|
||||
|
||||
if (!mout->dig_out_nid)
|
||||
return 0;
|
||||
|
||||
kctl = snd_ctl_new1(&spdif_share_sw, mout);
|
||||
kctl = snd_ctl_new1(&spdif_share_sw, codec);
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
/* ATTENTION: here mout is passed as private_data, instead of codec */
|
||||
return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
|
||||
/* snd_ctl_new1() stores @codec in private_data; stash @mout in
|
||||
* private_value for the share-switch callbacks and cache the
|
||||
* assigned control for forced-disable notifications.
|
||||
*/
|
||||
kctl->private_value = (unsigned long)mout;
|
||||
err = snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mout->share_spdif_kctl = kctl;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
|
||||
|
||||
@@ -2768,9 +2793,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
|
||||
if (codec->power_filter)
|
||||
state = codec->power_filter(codec, fg, state);
|
||||
if (state == power_state || power_state != AC_PWRST_D3)
|
||||
snd_hda_codec_read(codec, fg, flags,
|
||||
AC_VERB_SET_POWER_STATE,
|
||||
state);
|
||||
snd_hda_codec_write_sync(codec, fg, flags,
|
||||
AC_VERB_SET_POWER_STATE,
|
||||
state);
|
||||
snd_hda_codec_set_power_to_all(codec, fg, power_state);
|
||||
}
|
||||
state = snd_hda_sync_power_state(codec, fg, power_state);
|
||||
@@ -3701,6 +3726,8 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
|
||||
struct hda_pcm_stream *hinfo)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
bool notify_share_sw = false;
|
||||
|
||||
runtime->hw.channels_max = mout->max_channels;
|
||||
if (mout->dig_out_nid) {
|
||||
if (!mout->analog_rates) {
|
||||
@@ -3729,10 +3756,12 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
|
||||
hinfo->maxbps = mout->spdif_maxbps;
|
||||
} else {
|
||||
mout->share_spdif = 0;
|
||||
/* FIXME: need notify? */
|
||||
notify_share_sw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notify_share_sw)
|
||||
notify_spdif_share_sw(codec, mout);
|
||||
return snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
}
|
||||
@@ -4023,6 +4052,35 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_set_gpio - Set up GPIO bits for AFG
|
||||
* @codec: the HDA codec
|
||||
* @mask: GPIO bitmask
|
||||
* @dir: GPIO direction bits
|
||||
* @data: GPIO data bits
|
||||
* @delay: the delay in msec before writing GPIO data bits
|
||||
*/
|
||||
void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask,
|
||||
unsigned int dir, unsigned int data,
|
||||
unsigned int delay)
|
||||
{
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_MASK, mask);
|
||||
if (delay) {
|
||||
snd_hda_codec_write_sync(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, dir);
|
||||
msleep(delay);
|
||||
snd_hda_codec_write_sync(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_DATA, data);
|
||||
} else {
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, dir);
|
||||
snd_hda_codec_write(codec, codec->core.afg, 0,
|
||||
AC_VERB_SET_GPIO_DATA, data);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_set_gpio);
|
||||
|
||||
/**
|
||||
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
|
||||
* @pcm: PCM caps bits
|
||||
|
||||
@@ -221,6 +221,7 @@ struct hda_multi_out {
|
||||
unsigned int spdif_rates;
|
||||
unsigned int spdif_maxbps;
|
||||
u64 spdif_formats;
|
||||
struct snd_kcontrol *share_spdif_kctl; /* cached shared SPDIF switch */
|
||||
};
|
||||
|
||||
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
|
||||
|
||||
@@ -640,41 +640,78 @@ static void print_gpio(struct snd_info_buffer *buffer,
|
||||
{
|
||||
unsigned int gpio =
|
||||
param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
|
||||
unsigned int enable, direction, wake, unsol, sticky, data;
|
||||
int i, max;
|
||||
int i, gpio_max, gpo_max, gpi_max;
|
||||
|
||||
gpio_max = gpio & AC_GPIO_IO_COUNT;
|
||||
gpo_max = (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT;
|
||||
gpi_max = (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT;
|
||||
|
||||
snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
|
||||
"unsolicited=%d, wake=%d\n",
|
||||
gpio & AC_GPIO_IO_COUNT,
|
||||
(gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
|
||||
(gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
|
||||
gpio_max, gpo_max, gpi_max,
|
||||
(gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
|
||||
(gpio & AC_GPIO_WAKE) ? 1 : 0);
|
||||
max = gpio & AC_GPIO_IO_COUNT;
|
||||
if (!max || max > 8)
|
||||
return;
|
||||
enable = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_MASK, 0);
|
||||
direction = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_DIRECTION, 0);
|
||||
wake = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_WAKE_MASK, 0);
|
||||
unsol = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
|
||||
sticky = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_STICKY_MASK, 0);
|
||||
data = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_DATA, 0);
|
||||
for (i = 0; i < max; ++i)
|
||||
snd_iprintf(buffer,
|
||||
" IO[%d]: enable=%d, dir=%d, wake=%d, "
|
||||
"sticky=%d, data=%d, unsol=%d\n", i,
|
||||
(enable & (1<<i)) ? 1 : 0,
|
||||
(direction & (1<<i)) ? 1 : 0,
|
||||
(wake & (1<<i)) ? 1 : 0,
|
||||
(sticky & (1<<i)) ? 1 : 0,
|
||||
(data & (1<<i)) ? 1 : 0,
|
||||
(unsol & (1<<i)) ? 1 : 0);
|
||||
/* FIXME: add GPO and GPI pin information */
|
||||
|
||||
if (gpio_max && gpio_max <= 8) {
|
||||
unsigned int enable, direction, wake, unsol, sticky, data;
|
||||
|
||||
enable = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_MASK, 0);
|
||||
direction = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_DIRECTION, 0);
|
||||
wake = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_WAKE_MASK, 0);
|
||||
unsol = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK,
|
||||
0);
|
||||
sticky = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_STICKY_MASK, 0);
|
||||
data = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPIO_DATA, 0);
|
||||
for (i = 0; i < gpio_max; ++i) {
|
||||
snd_iprintf(buffer,
|
||||
" IO[%d]: enable=%d, dir=%d, wake=%d, ",
|
||||
i, (enable & (1 << i)) ? 1 : 0,
|
||||
(direction & (1 << i)) ? 1 : 0,
|
||||
(wake & (1 << i)) ? 1 : 0);
|
||||
snd_iprintf(buffer,
|
||||
"sticky=%d, data=%d, unsol=%d\n",
|
||||
(sticky & (1 << i)) ? 1 : 0,
|
||||
(data & (1 << i)) ? 1 : 0,
|
||||
(unsol & (1 << i)) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (gpo_max && gpo_max <= 8) {
|
||||
unsigned int gpo_data;
|
||||
|
||||
gpo_data = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPO_DATA, 0);
|
||||
for (i = 0; i < gpo_max; ++i)
|
||||
snd_iprintf(buffer, " GPO[%d]: data=%d\n", i,
|
||||
(gpo_data & (1 << i)) ? 1 : 0);
|
||||
}
|
||||
|
||||
if (gpi_max && gpi_max <= 8) {
|
||||
unsigned int wake, unsol, sticky, data;
|
||||
|
||||
wake = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPI_WAKE_MASK, 0);
|
||||
unsol = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK,
|
||||
0);
|
||||
sticky = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPI_STICKY_MASK, 0);
|
||||
data = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_GPI_DATA, 0);
|
||||
for (i = 0; i < gpi_max; ++i)
|
||||
snd_iprintf(buffer, " GPI[%d]: wake=%d, sticky=%d, data=%d, unsol=%d\n",
|
||||
i, (wake & (1 << i)) ? 1 : 0,
|
||||
(sticky & (1 << i)) ? 1 : 0,
|
||||
(data & (1 << i)) ? 1 : 0,
|
||||
(unsol & (1 << i)) ? 1 : 0);
|
||||
}
|
||||
|
||||
print_nid_array(buffer, codec, nid, &codec->mixers);
|
||||
print_nid_array(buffer, codec, nid, &codec->nids);
|
||||
}
|
||||
@@ -940,4 +977,3 @@ int snd_hda_codec_proc_new(struct hda_codec *codec)
|
||||
snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
|
||||
return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -2436,20 +2436,7 @@ static void azx_remove(struct pci_dev *pci)
|
||||
/* cancel the pending probing work */
|
||||
chip = card->private_data;
|
||||
hda = container_of(chip, struct hda_intel, chip);
|
||||
/* FIXME: below is an ugly workaround.
|
||||
* Both device_release_driver() and driver_probe_device()
|
||||
* take *both* the device's and its parent's lock before
|
||||
* calling the remove() and probe() callbacks. The codec
|
||||
* probe takes the locks of both the codec itself and its
|
||||
* parent, i.e. the PCI controller dev. Meanwhile, when
|
||||
* the PCI controller is unbound, it takes its lock, too
|
||||
* ==> ouch, a deadlock!
|
||||
* As a workaround, we unlock temporarily here the controller
|
||||
* device during cancel_work_sync() call.
|
||||
*/
|
||||
device_unlock(&pci->dev);
|
||||
cancel_delayed_work_sync(&hda->probe_work);
|
||||
device_lock(&pci->dev);
|
||||
|
||||
clear_bit(chip->dev_index, probed_devs);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
|
||||
@@ -53,6 +53,31 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state)
|
||||
}
|
||||
}
|
||||
|
||||
/* reset procedure for AK4529 */
|
||||
static void ak4529_reset(struct snd_akm4xxx *ak, int state)
|
||||
{
|
||||
static const unsigned char regs[] = {
|
||||
0x0a, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x0b, 0x0c, 0x08,
|
||||
};
|
||||
unsigned int i;
|
||||
unsigned char reg;
|
||||
|
||||
if (state) {
|
||||
snd_akm4xxx_write(ak, 0, 0x09,
|
||||
snd_akm4xxx_get(ak, 0, 0x09) & ~0x01);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
reg = regs[i];
|
||||
snd_akm4xxx_write(ak, 0, reg,
|
||||
snd_akm4xxx_get(ak, 0, reg));
|
||||
}
|
||||
snd_akm4xxx_write(ak, 0, 0x09,
|
||||
snd_akm4xxx_get(ak, 0, 0x09) | 0x01);
|
||||
}
|
||||
|
||||
/* reset procedure for AK4355 and AK4358 */
|
||||
static void ak435X_reset(struct snd_akm4xxx *ak, int state)
|
||||
{
|
||||
@@ -99,7 +124,7 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
|
||||
ak4524_reset(ak, state);
|
||||
break;
|
||||
case SND_AK4529:
|
||||
/* FIXME: needed for ak4529? */
|
||||
ak4529_reset(ak, state);
|
||||
break;
|
||||
case SND_AK4355:
|
||||
ak435X_reset(ak, state);
|
||||
@@ -256,6 +281,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
|
||||
0x07, 0x00, /* 7: ROUT muted */
|
||||
0xff, 0xff
|
||||
};
|
||||
static const unsigned char ak5365_defaults[] = {
|
||||
0x01, 0x00, 0x00, 0x2b, 0x7f, 0x7f, 0x28, 0x89,
|
||||
};
|
||||
|
||||
int chip;
|
||||
const unsigned char *ptr, *inits;
|
||||
@@ -302,10 +330,12 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
|
||||
ak->total_regs = 0x05;
|
||||
break;
|
||||
case SND_AK5365:
|
||||
/* FIXME: any init sequence? */
|
||||
ak->num_chips = 1;
|
||||
ak->name = "ak5365";
|
||||
ak->total_regs = 0x08;
|
||||
memcpy(ak->images, ak5365_defaults, sizeof(ak5365_defaults));
|
||||
snd_akm4xxx_set_vol(ak, 0, 0x04, 127);
|
||||
snd_akm4xxx_set_vol(ak, 0, 0x05, 127);
|
||||
return;
|
||||
case SND_AK4620:
|
||||
inits = inits_ak4620;
|
||||
|
||||
@@ -51,6 +51,7 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer)
|
||||
snd_i2c_unlock(bus);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_tea6330t_detect);
|
||||
|
||||
#if 0
|
||||
static void snd_tea6330t_set(struct tea6330t *tea,
|
||||
@@ -355,6 +356,42 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
|
||||
snd_i2c_device_free(device);
|
||||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_tea6330t_detect);
|
||||
EXPORT_SYMBOL(snd_tea6330t_update_mixer);
|
||||
|
||||
int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus)
|
||||
{
|
||||
struct snd_i2c_device *device;
|
||||
struct tea6330t *tea;
|
||||
unsigned char bytes[7];
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
if (!bus)
|
||||
return -EINVAL;
|
||||
|
||||
snd_i2c_lock(bus);
|
||||
list_for_each_entry(device, &bus->devices, list) {
|
||||
if (device->addr != TEA6330T_ADDR)
|
||||
continue;
|
||||
|
||||
tea = device->private_data;
|
||||
if (!tea) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
|
||||
for (idx = 0; idx < 6; idx++)
|
||||
bytes[idx + 1] = tea->regs[idx];
|
||||
err = snd_i2c_sendbytes(device, bytes, 7);
|
||||
err = err < 0 ? err : 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
unlock:
|
||||
snd_i2c_unlock(bus);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_tea6330t_restore_mixer);
|
||||
|
||||
@@ -184,12 +184,44 @@ static int snd_es1688_isa_probe(struct device *dev, unsigned int n)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_es1688_card_suspend(struct snd_card *card)
|
||||
{
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_es1688_card_resume(struct snd_card *card)
|
||||
{
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_es1688_reset(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_es1688_isa_suspend(struct device *dev, unsigned int n,
|
||||
pm_message_t state)
|
||||
{
|
||||
return snd_es1688_card_suspend(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int snd_es1688_isa_resume(struct device *dev, unsigned int n)
|
||||
{
|
||||
return snd_es1688_card_resume(dev_get_drvdata(dev));
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_es1688_driver = {
|
||||
.match = snd_es1688_match,
|
||||
.probe = snd_es1688_isa_probe,
|
||||
#if 0 /* FIXME */
|
||||
.suspend = snd_es1688_suspend,
|
||||
.resume = snd_es1688_resume,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_es1688_isa_suspend,
|
||||
.resume = snd_es1688_isa_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DEV_NAME
|
||||
@@ -266,20 +298,12 @@ static void snd_es968_pnp_remove(struct pnp_card_link *pcard)
|
||||
static int snd_es968_pnp_suspend(struct pnp_card_link *pcard,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
return 0;
|
||||
return snd_es1688_card_suspend(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
|
||||
static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
|
||||
snd_es1688_reset(chip);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
return snd_es1688_card_resume(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -173,6 +173,39 @@ int snd_gf1_dma_done(struct snd_gus_card * gus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_gf1_dma_suspend(struct snd_gus_card *gus)
|
||||
{
|
||||
struct snd_gf1_dma_block *block;
|
||||
|
||||
guard(mutex)(&gus->dma_mutex);
|
||||
if (!gus->gf1.dma_shared)
|
||||
return;
|
||||
|
||||
snd_dma_disable(gus->gf1.dma1);
|
||||
snd_gf1_dma_ack(gus);
|
||||
if (gus->gf1.dma_ack)
|
||||
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
|
||||
gus->gf1.dma_ack = NULL;
|
||||
gus->gf1.dma_private_data = NULL;
|
||||
|
||||
while ((block = gus->gf1.dma_data_pcm)) {
|
||||
gus->gf1.dma_data_pcm = block->next;
|
||||
if (block->ack)
|
||||
block->ack(gus, block->private_data);
|
||||
kfree(block);
|
||||
}
|
||||
while ((block = gus->gf1.dma_data_synth)) {
|
||||
gus->gf1.dma_data_synth = block->next;
|
||||
if (block->ack)
|
||||
block->ack(gus, block->private_data);
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
gus->gf1.dma_data_pcm_last = NULL;
|
||||
gus->gf1.dma_data_synth_last = NULL;
|
||||
gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
|
||||
}
|
||||
|
||||
int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
|
||||
struct snd_gf1_dma_block * __block,
|
||||
int atomic,
|
||||
|
||||
@@ -404,6 +404,42 @@ int snd_gus_initialize(struct snd_gus_card *gus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_gus_suspend(struct snd_gus_card *gus)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (gus->pcm) {
|
||||
err = snd_pcm_suspend_all(gus->pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_gf1_suspend(gus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_power_change_state(gus->card, SNDRV_CTL_POWER_D3hot);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_gus_suspend);
|
||||
|
||||
int snd_gus_resume(struct snd_gus_card *gus)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_gus_init_dma_irq(gus, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_gf1_resume(gus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_power_change_state(gus->card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_gus_resume);
|
||||
|
||||
/* gus_io.c */
|
||||
EXPORT_SYMBOL(snd_gf1_delay);
|
||||
EXPORT_SYMBOL(snd_gf1_write8);
|
||||
|
||||
@@ -471,7 +471,8 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
snd_gf1_pcm_trigger_up(substream);
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP ||
|
||||
cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
|
||||
scoped_guard(spinlock, &pcmp->lock) {
|
||||
pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
|
||||
}
|
||||
@@ -558,7 +559,8 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
val = gus->gf1.pcm_rcntrl_reg;
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP ||
|
||||
cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
|
||||
val = 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
@@ -856,4 +858,3 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <asm/dma.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/gus.h>
|
||||
|
||||
@@ -263,11 +264,18 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
|
||||
private_free(voice);
|
||||
}
|
||||
|
||||
/*
|
||||
* call this function only by start of driver
|
||||
*/
|
||||
static void snd_gf1_init_software_state(struct snd_gus_card *gus)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
int snd_gf1_start(struct snd_gus_card * gus)
|
||||
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL);
|
||||
for (i = 0; i < 32; i++) {
|
||||
gus->gf1.voices[i].number = i;
|
||||
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_gf1_hw_start(struct snd_gus_card *gus, bool initial)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -277,14 +285,14 @@ int snd_gf1_start(struct snd_gus_card * gus)
|
||||
udelay(160);
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
|
||||
|
||||
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL);
|
||||
for (i = 0; i < 32; i++) {
|
||||
gus->gf1.voices[i].number = i;
|
||||
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i);
|
||||
if (initial) {
|
||||
snd_gf1_init_software_state(gus);
|
||||
snd_gf1_uart_cmd(gus, 0x03);
|
||||
} else {
|
||||
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
|
||||
outb(0x03, GUSP(gus, MIDICTRL));
|
||||
}
|
||||
|
||||
snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */
|
||||
|
||||
if (gus->gf1.enh_mode) { /* enhanced mode !!!! */
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
|
||||
@@ -293,6 +301,8 @@ int snd_gf1_start(struct snd_gus_card * gus)
|
||||
snd_gf1_select_active_voices(gus);
|
||||
snd_gf1_delay(gus);
|
||||
gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8;
|
||||
gus->gf1.hw_lfo = 0;
|
||||
gus->gf1.sw_lfo = 0;
|
||||
/* initialize LFOs & clear LFOs memory */
|
||||
if (gus->gf1.enh_mode && gus->gf1.memory) {
|
||||
gus->gf1.hw_lfo = 1;
|
||||
@@ -321,7 +331,15 @@ int snd_gf1_start(struct snd_gus_card * gus)
|
||||
outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
|
||||
outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
|
||||
}
|
||||
}
|
||||
|
||||
int snd_gf1_start(struct snd_gus_card *gus)
|
||||
{
|
||||
/*
|
||||
* Probe-time startup initializes both GF1 hardware and the
|
||||
* software state that suspend/resume keeps across PM cycles.
|
||||
*/
|
||||
snd_gf1_hw_start(gus, true);
|
||||
snd_gf1_timers_init(gus);
|
||||
snd_gf1_look_regs(gus);
|
||||
snd_gf1_mem_init(gus);
|
||||
@@ -357,3 +375,27 @@ int snd_gf1_stop(struct snd_gus_card * gus)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_gf1_suspend(struct snd_gus_card *gus)
|
||||
{
|
||||
snd_gf1_dma_suspend(gus);
|
||||
snd_gf1_uart_suspend(gus);
|
||||
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0);
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);
|
||||
snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
|
||||
snd_gf1_stop_voices(gus, 0, 31);
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);
|
||||
snd_dma_disable(gus->gf1.dma2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_gf1_resume(struct snd_gus_card *gus)
|
||||
{
|
||||
snd_gf1_hw_start(gus, false);
|
||||
snd_gf1_timers_resume(gus);
|
||||
snd_gf1_uart_resume(gus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -178,3 +178,17 @@ void snd_gf1_timers_done(struct snd_gus_card * gus)
|
||||
gus->gf1.timer2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void snd_gf1_timers_resume(struct snd_gus_card *gus)
|
||||
{
|
||||
if (gus->gf1.timer1) {
|
||||
gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
|
||||
if (gus->gf1.timer_enabled & 4)
|
||||
snd_gf1_timer1_start(gus->gf1.timer1);
|
||||
}
|
||||
if (gus->gf1.timer2) {
|
||||
gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
|
||||
if (gus->gf1.timer_enabled & 8)
|
||||
snd_gf1_timer2_start(gus->gf1.timer2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,3 +232,50 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
|
||||
gus->midi_uart = rmidi;
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_gf1_uart_suspend(struct snd_gus_card *gus)
|
||||
{
|
||||
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
|
||||
outb(0x03, GUSP(gus, MIDICTRL));
|
||||
}
|
||||
|
||||
void snd_gf1_uart_resume(struct snd_gus_card *gus)
|
||||
{
|
||||
unsigned short uart_cmd;
|
||||
bool active;
|
||||
int i;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &gus->uart_cmd_lock) {
|
||||
active = gus->midi_substream_input || gus->midi_substream_output;
|
||||
}
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
/* snd_gf1_hw_start() already left MIDICTRL in reset. */
|
||||
usleep_range(160, 200);
|
||||
|
||||
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
|
||||
if (!gus->midi_substream_input && !gus->midi_substream_output)
|
||||
return;
|
||||
|
||||
if (gus->midi_substream_output)
|
||||
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
|
||||
if (gus->midi_substream_input)
|
||||
gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
|
||||
|
||||
if (!gus->uart_enable)
|
||||
return;
|
||||
|
||||
uart_cmd = gus->gf1.uart_cmd;
|
||||
snd_gf1_uart_cmd(gus, 0x00);
|
||||
|
||||
if (gus->midi_substream_input) {
|
||||
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
|
||||
snd_gf1_uart_get(gus);
|
||||
if (i >= 1000)
|
||||
dev_err(gus->card->dev,
|
||||
"gus midi uart resume - cleanup error\n");
|
||||
}
|
||||
|
||||
snd_gf1_uart_cmd(gus, uart_cmd);
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n)
|
||||
error = snd_gusclassic_create(card, dev, n, &gus);
|
||||
if (error < 0)
|
||||
return error;
|
||||
card->private_data = gus;
|
||||
|
||||
error = snd_gusclassic_detect(gus);
|
||||
if (error < 0)
|
||||
@@ -193,11 +194,29 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_gusclassic_suspend(struct device *dev, unsigned int n,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
|
||||
return snd_gus_suspend(card->private_data);
|
||||
}
|
||||
|
||||
static int snd_gusclassic_resume(struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
|
||||
return snd_gus_resume(card->private_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_gusclassic_driver = {
|
||||
.match = snd_gusclassic_match,
|
||||
.probe = snd_gusclassic_probe,
|
||||
#if 0 /* FIXME */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_gusclassic_suspend,
|
||||
.resume = snd_gusclassic_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DEV_NAME
|
||||
|
||||
@@ -44,6 +44,11 @@ static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
|
||||
static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
|
||||
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
|
||||
|
||||
struct snd_gusextreme {
|
||||
struct snd_es1688 es1688;
|
||||
struct snd_gus_card *gus;
|
||||
};
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
@@ -142,17 +147,15 @@ static int snd_gusextreme_gus_card_create(struct snd_card *card,
|
||||
0, channels[n], pcm_channels[n], 0, rgus);
|
||||
}
|
||||
|
||||
static int snd_gusextreme_detect(struct snd_gus_card *gus,
|
||||
struct snd_es1688 *es1688)
|
||||
static void snd_gusextreme_enable_gf1(struct snd_gus_card *gus,
|
||||
struct snd_es1688 *es1688)
|
||||
{
|
||||
unsigned char d;
|
||||
|
||||
/*
|
||||
* This is main stuff - enable access to GF1 chip...
|
||||
* I'm not sure, if this will work for card which have
|
||||
* ES1688 chip in another place than 0x220.
|
||||
*
|
||||
* I used reverse-engineering in DOSEMU. [--jk]
|
||||
*
|
||||
* I used reverse-engineering in DOSEMU. [--jk]
|
||||
*
|
||||
* ULTRINIT.EXE:
|
||||
* 0x230 = 0,2,3
|
||||
@@ -172,7 +175,14 @@ static int snd_gusextreme_detect(struct snd_gus_card *gus,
|
||||
outb(0, 0x201);
|
||||
outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_gusextreme_detect(struct snd_gus_card *gus,
|
||||
struct snd_es1688 *es1688)
|
||||
{
|
||||
unsigned char d;
|
||||
|
||||
snd_gusextreme_enable_gf1(gus, es1688);
|
||||
udelay(100);
|
||||
|
||||
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
|
||||
@@ -223,16 +233,18 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_gus_card *gus;
|
||||
struct snd_gusextreme *gusextreme;
|
||||
struct snd_es1688 *es1688;
|
||||
struct snd_opl3 *opl3;
|
||||
int error;
|
||||
|
||||
error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
|
||||
sizeof(struct snd_es1688), &card);
|
||||
sizeof(*gusextreme), &card);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
es1688 = card->private_data;
|
||||
gusextreme = card->private_data;
|
||||
es1688 = &gusextreme->es1688;
|
||||
|
||||
if (mpu_port[n] == SNDRV_AUTO_PORT)
|
||||
mpu_port[n] = 0;
|
||||
@@ -250,6 +262,7 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
error = snd_gusextreme_gus_card_create(card, dev, n, &gus);
|
||||
if (error < 0)
|
||||
return error;
|
||||
gusextreme->gus = gus;
|
||||
|
||||
error = snd_gusextreme_detect(gus, es1688);
|
||||
if (error < 0)
|
||||
@@ -321,10 +334,36 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_gusextreme_suspend(struct device *dev, unsigned int n,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_gusextreme *gusextreme = card->private_data;
|
||||
|
||||
return snd_gus_suspend(gusextreme->gus);
|
||||
}
|
||||
|
||||
static int snd_gusextreme_resume(struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_gusextreme *gusextreme = card->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_es1688_reset(&gusextreme->es1688);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_gusextreme_enable_gf1(gusextreme->gus, &gusextreme->es1688);
|
||||
usleep_range(100, 200);
|
||||
return snd_gus_resume(gusextreme->gus);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_gusextreme_driver = {
|
||||
.match = snd_gusextreme_match,
|
||||
.probe = snd_gusextreme_probe,
|
||||
#if 0 /* FIXME */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_gusextreme_suspend,
|
||||
.resume = snd_gusextreme_resume,
|
||||
#endif
|
||||
|
||||
@@ -328,12 +328,38 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_gusmax_suspend(struct device *dev, unsigned int n,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_gusmax *maxcard = card->private_data;
|
||||
|
||||
maxcard->wss->suspend(maxcard->wss);
|
||||
return snd_gus_suspend(maxcard->gus);
|
||||
}
|
||||
|
||||
static int snd_gusmax_resume(struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_gusmax *maxcard = card->private_data;
|
||||
|
||||
/* Restore the board routing latch before resuming the codec and GF1. */
|
||||
outb(maxcard->gus->max_cntrl_val, GUSP(maxcard->gus, MAXCNTRLPORT));
|
||||
maxcard->wss->resume(maxcard->wss);
|
||||
return snd_gus_resume(maxcard->gus);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEV_NAME "gusmax"
|
||||
|
||||
static struct isa_driver snd_gusmax_driver = {
|
||||
.match = snd_gusmax_match,
|
||||
.probe = snd_gusmax_probe,
|
||||
/* FIXME: suspend/resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_gusmax_suspend,
|
||||
.resume = snd_gusmax_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DEV_NAME
|
||||
},
|
||||
|
||||
@@ -96,6 +96,7 @@ struct snd_interwave {
|
||||
struct snd_gus_card *gus;
|
||||
struct snd_wss *wss;
|
||||
#ifdef SNDRV_STB
|
||||
struct snd_i2c_bus *i2c_bus;
|
||||
struct resource *i2c_res;
|
||||
#endif
|
||||
unsigned short gus_status_reg;
|
||||
@@ -363,18 +364,30 @@ struct rom_hdr {
|
||||
/* 511 */ unsigned char csum;
|
||||
};
|
||||
|
||||
static const unsigned int snd_interwave_memory_configs[] = {
|
||||
0x00000001, 0x00000101, 0x01010101, 0x00000401,
|
||||
0x04040401, 0x00040101, 0x04040101, 0x00000004,
|
||||
0x00000404, 0x04040404, 0x00000010, 0x00001010,
|
||||
0x10101010
|
||||
};
|
||||
|
||||
static int snd_interwave_find_memory_config(unsigned int lmct)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_interwave_memory_configs); i++) {
|
||||
if (lmct == snd_interwave_memory_configs[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void snd_interwave_detect_memory(struct snd_gus_card *gus)
|
||||
{
|
||||
static const unsigned int lmc[13] =
|
||||
{
|
||||
0x00000001, 0x00000101, 0x01010101, 0x00000401,
|
||||
0x04040401, 0x00040101, 0x04040101, 0x00000004,
|
||||
0x00000404, 0x04040404, 0x00000010, 0x00001010,
|
||||
0x10101010
|
||||
};
|
||||
|
||||
int bank_pos, pages;
|
||||
unsigned int i, lmct;
|
||||
int lmc_cfg;
|
||||
int psizes[4];
|
||||
unsigned char iwave[8];
|
||||
unsigned char csum;
|
||||
@@ -399,17 +412,20 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
|
||||
#if 0
|
||||
dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct);
|
||||
#endif
|
||||
for (i = 0; i < ARRAY_SIZE(lmc); i++)
|
||||
if (lmct == lmc[i]) {
|
||||
lmc_cfg = snd_interwave_find_memory_config(lmct);
|
||||
if (lmc_cfg >= 0) {
|
||||
#if 0
|
||||
dev_dbg(gus->card->dev, "found !!! %i\n", i);
|
||||
dev_dbg(gus->card->dev, "found !!! %i\n", lmc_cfg);
|
||||
#endif
|
||||
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
|
||||
snd_interwave_bank_sizes(gus, psizes);
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode)
|
||||
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2);
|
||||
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG,
|
||||
(snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) |
|
||||
lmc_cfg);
|
||||
snd_interwave_bank_sizes(gus, psizes);
|
||||
} else if (!gus->gf1.enh_mode) {
|
||||
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG,
|
||||
(snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) |
|
||||
2);
|
||||
}
|
||||
for (i = 0; i < 4; i++) {
|
||||
gus->gf1.mem_alloc.banks_8[i].address =
|
||||
gus->gf1.mem_alloc.banks_16[i].address = i << 22;
|
||||
@@ -454,24 +470,28 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
|
||||
snd_interwave_reset(gus);
|
||||
}
|
||||
|
||||
static void __snd_interwave_restore_regs(struct snd_gus_card *gus)
|
||||
{
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
|
||||
}
|
||||
|
||||
static void snd_interwave_init(int dev, struct snd_gus_card *gus)
|
||||
{
|
||||
/* ok.. some InterWave specific initialization */
|
||||
/* Probe-time setup also clears the timer control register. */
|
||||
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
|
||||
snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
|
||||
__snd_interwave_restore_regs(gus);
|
||||
}
|
||||
gus->equal_irq = 1;
|
||||
gus->codec_flag = 1;
|
||||
gus->interwave = 1;
|
||||
gus->max_flag = 1;
|
||||
gus->joystick_dac = joystick_dac[dev];
|
||||
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_interwave_controls[] = {
|
||||
@@ -724,6 +744,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev,
|
||||
err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
iwcard->i2c_bus = i2c_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -828,10 +849,97 @@ static int snd_interwave_isa_probe(struct device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void snd_interwave_restore_regs(struct snd_gus_card *gus)
|
||||
{
|
||||
scoped_guard(spinlock_irqsave, &gus->reg_lock)
|
||||
__snd_interwave_restore_regs(gus);
|
||||
}
|
||||
|
||||
static void snd_interwave_restore_memory(struct snd_gus_card *gus)
|
||||
{
|
||||
unsigned short mem_cfg;
|
||||
unsigned int lmct = 0;
|
||||
int i, lmc_cfg;
|
||||
|
||||
if (!gus->gf1.memory)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8);
|
||||
|
||||
lmc_cfg = snd_interwave_find_memory_config(lmct);
|
||||
if (lmc_cfg < 0) {
|
||||
if (!gus->gf1.enh_mode) {
|
||||
lmc_cfg = 2;
|
||||
} else {
|
||||
dev_warn(gus->card->dev,
|
||||
"cannot restore InterWave memory layout 0x%08x\n",
|
||||
lmct);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
|
||||
mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG);
|
||||
mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg;
|
||||
mem_cfg = (mem_cfg & 0xff1f) | (4 << 5);
|
||||
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_interwave_card_suspend(struct snd_card *card)
|
||||
{
|
||||
struct snd_interwave *iwcard = card->private_data;
|
||||
|
||||
iwcard->wss->suspend(iwcard->wss);
|
||||
return snd_gus_suspend(iwcard->gus);
|
||||
}
|
||||
|
||||
static int snd_interwave_card_resume(struct snd_card *card)
|
||||
{
|
||||
struct snd_interwave *iwcard = card->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_gus_resume(iwcard->gus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_interwave_restore_regs(iwcard->gus);
|
||||
snd_interwave_restore_memory(iwcard->gus);
|
||||
iwcard->wss->resume(iwcard->wss);
|
||||
#ifdef SNDRV_STB
|
||||
if (iwcard->i2c_bus) {
|
||||
err = snd_tea6330t_restore_mixer(iwcard->i2c_bus);
|
||||
if (err < 0)
|
||||
dev_warn(card->dev,
|
||||
"failed to restore TEA6330T mixer state: %d\n",
|
||||
err);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_interwave_isa_suspend(struct device *pdev, unsigned int dev,
|
||||
pm_message_t state)
|
||||
{
|
||||
return snd_interwave_card_suspend(dev_get_drvdata(pdev));
|
||||
}
|
||||
|
||||
static int snd_interwave_isa_resume(struct device *pdev, unsigned int dev)
|
||||
{
|
||||
return snd_interwave_card_resume(dev_get_drvdata(pdev));
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_interwave_driver = {
|
||||
.match = snd_interwave_isa_match,
|
||||
.probe = snd_interwave_isa_probe,
|
||||
/* FIXME: suspend,resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_interwave_isa_suspend,
|
||||
.resume = snd_interwave_isa_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = INTERWAVE_DRIVER
|
||||
},
|
||||
@@ -871,12 +979,28 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_interwave_pnpc_suspend(struct pnp_card_link *pcard,
|
||||
pm_message_t state)
|
||||
{
|
||||
return snd_interwave_card_suspend(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
|
||||
static int snd_interwave_pnpc_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
return snd_interwave_card_resume(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pnp_card_driver interwave_pnpc_driver = {
|
||||
.flags = PNP_DRIVER_RES_DISABLE,
|
||||
.name = INTERWAVE_PNP_DRIVER,
|
||||
.id_table = snd_interwave_pnpids,
|
||||
.probe = snd_interwave_pnp_detect,
|
||||
/* FIXME: suspend,resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_interwave_pnpc_suspend,
|
||||
.resume = snd_interwave_pnpc_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* CONFIG_PNP */
|
||||
|
||||
@@ -127,11 +127,8 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_upload_host);
|
||||
|
||||
int snd_msnd_enable_irq(struct snd_msnd *dev)
|
||||
static int __snd_msnd_enable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
if (dev->irq_ref++)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n");
|
||||
|
||||
guard(spinlock_irqsave)(&dev->lock);
|
||||
@@ -152,17 +149,9 @@ int snd_msnd_enable_irq(struct snd_msnd *dev)
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_enable_irq);
|
||||
|
||||
int snd_msnd_disable_irq(struct snd_msnd *dev)
|
||||
static int __snd_msnd_disable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
if (--dev->irq_ref > 0)
|
||||
return 0;
|
||||
|
||||
if (dev->irq_ref < 0)
|
||||
dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
|
||||
dev->irq_ref);
|
||||
|
||||
dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n");
|
||||
|
||||
guard(spinlock_irqsave)(&dev->lock);
|
||||
@@ -178,8 +167,39 @@ int snd_msnd_disable_irq(struct snd_msnd *dev)
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int snd_msnd_enable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
if (dev->irq_ref++)
|
||||
return 0;
|
||||
|
||||
return __snd_msnd_enable_irq(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_enable_irq);
|
||||
|
||||
int snd_msnd_disable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
if (--dev->irq_ref > 0)
|
||||
return 0;
|
||||
|
||||
if (dev->irq_ref < 0)
|
||||
dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
|
||||
dev->irq_ref);
|
||||
|
||||
return __snd_msnd_disable_irq(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_disable_irq);
|
||||
|
||||
int snd_msnd_force_irq(struct snd_msnd *dev, bool enable)
|
||||
{
|
||||
if (!dev->irq_ref)
|
||||
return 0;
|
||||
|
||||
return enable ? __snd_msnd_enable_irq(dev) :
|
||||
__snd_msnd_disable_irq(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_force_irq);
|
||||
|
||||
static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
|
||||
{
|
||||
long tmp = (size * HZ * chip->play_sample_size) / 8;
|
||||
@@ -507,25 +527,27 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
int result = 0;
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
dev_dbg(chip->card->dev, "%s(START)\n", __func__);
|
||||
chip->banksPlayed = 0;
|
||||
set_bit(F_WRITING, &chip->flags);
|
||||
snd_msnd_DAPQ(chip, 1);
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
dev_dbg(chip->card->dev, "%s(STOP)\n", __func__);
|
||||
/* interrupt diagnostic, comment this out later */
|
||||
clear_bit(F_WRITING, &chip->flags);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
dev_dbg(chip->card->dev, "%s(?????)\n", __func__);
|
||||
result = -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(chip->card->dev, "%s() ENDE\n", __func__);
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
@@ -589,17 +611,22 @@ static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
chip->last_recbank = -1;
|
||||
set_bit(F_READING, &chip->flags);
|
||||
if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
|
||||
return 0;
|
||||
|
||||
clear_bit(F_READING, &chip->flags);
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
clear_bit(F_READING, &chip->flags);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -668,4 +695,3 @@ EXPORT_SYMBOL(snd_msnd_pcm);
|
||||
|
||||
MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
@@ -253,6 +253,8 @@ struct snd_msnd {
|
||||
spinlock_t mixer_lock;
|
||||
int nresets;
|
||||
unsigned recsrc;
|
||||
u8 pm_recsrc;
|
||||
bool pm_mpu_input;
|
||||
#define LEVEL_ENTRIES 32
|
||||
int left_levels[LEVEL_ENTRIES];
|
||||
int right_levels[LEVEL_ENTRIES];
|
||||
@@ -280,6 +282,7 @@ int snd_msnd_upload_host(struct snd_msnd *chip,
|
||||
const u8 *bin, int len);
|
||||
int snd_msnd_enable_irq(struct snd_msnd *chip);
|
||||
int snd_msnd_disable_irq(struct snd_msnd *chip);
|
||||
int snd_msnd_force_irq(struct snd_msnd *chip, bool enable);
|
||||
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
|
||||
int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
|
||||
int snd_msnd_DARQ(struct snd_msnd *chip, int start);
|
||||
|
||||
@@ -513,6 +513,19 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
|
||||
snd_msnd_disable_irq(mpu->private_data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static u8 snd_msnd_pm_recsrc(struct snd_msnd *chip)
|
||||
{
|
||||
/* Convert recsrc to the Capture Source selector: 0=Analog, 1=MASS, 2=SPDIF. */
|
||||
if (chip->recsrc & BIT(4))
|
||||
return 1;
|
||||
if ((chip->recsrc & BIT(17)) &&
|
||||
test_bit(F_HAVEDIGITAL, &chip->flags))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
||||
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
|
||||
|
||||
@@ -1001,10 +1014,73 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_msnd_card_suspend(struct snd_card *card)
|
||||
{
|
||||
struct snd_msnd *chip = card->private_data;
|
||||
struct snd_mpu401 *mpu;
|
||||
int err;
|
||||
|
||||
mpu = chip->rmidi ? chip->rmidi->private_data : NULL;
|
||||
chip->pm_recsrc = snd_msnd_pm_recsrc(chip);
|
||||
chip->pm_mpu_input = mpu && test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
|
||||
if (chip->pm_mpu_input)
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_STOP);
|
||||
|
||||
err = snd_msnd_force_irq(chip, false);
|
||||
if (err < 0) {
|
||||
if (chip->pm_mpu_input)
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START);
|
||||
return err;
|
||||
}
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_card_resume(struct snd_card *card)
|
||||
{
|
||||
struct snd_msnd *chip = card->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_msnd_initialize(card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_msnd_calibrate_adc(chip, chip->play_sample_rate);
|
||||
snd_msndmix_force_recsrc(chip, chip->pm_recsrc);
|
||||
|
||||
err = snd_msnd_force_irq(chip, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (chip->pm_mpu_input)
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START);
|
||||
|
||||
chip->nresets = 0;
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_isa_suspend(struct device *dev, unsigned int idx,
|
||||
pm_message_t state)
|
||||
{
|
||||
return snd_msnd_card_suspend(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int snd_msnd_isa_resume(struct device *dev, unsigned int idx)
|
||||
{
|
||||
return snd_msnd_card_resume(dev_get_drvdata(dev));
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_msnd_driver = {
|
||||
.match = snd_msnd_isa_match,
|
||||
.probe = snd_msnd_isa_probe,
|
||||
/* FIXME: suspend, resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_msnd_isa_suspend,
|
||||
.resume = snd_msnd_isa_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DEV_NAME
|
||||
},
|
||||
@@ -1111,6 +1187,18 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_msnd_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
|
||||
{
|
||||
return snd_msnd_card_suspend(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
|
||||
static int snd_msnd_pnp_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
return snd_msnd_card_resume(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int isa_registered;
|
||||
static int pnp_registered;
|
||||
|
||||
@@ -1127,6 +1215,10 @@ static struct pnp_card_driver msnd_pnpc_driver = {
|
||||
.name = "msnd_pinnacle",
|
||||
.id_table = msnd_pnpids,
|
||||
.probe = snd_msnd_pnp_detect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_msnd_pnp_suspend,
|
||||
.resume = snd_msnd_pnp_resume,
|
||||
#endif
|
||||
};
|
||||
#endif /* CONFIG_PNP */
|
||||
|
||||
@@ -1161,4 +1253,3 @@ static void __exit snd_msnd_exit(void)
|
||||
|
||||
module_init(snd_msnd_init);
|
||||
module_exit(snd_msnd_exit);
|
||||
|
||||
|
||||
@@ -310,6 +310,10 @@ EXPORT_SYMBOL(snd_msndmix_new);
|
||||
|
||||
void snd_msndmix_setup(struct snd_msnd *dev)
|
||||
{
|
||||
writew(dev->left_levels[MSND_MIXER_VOLUME],
|
||||
dev->SMA + SMA_wCurrMastVolLeft);
|
||||
writew(dev->right_levels[MSND_MIXER_VOLUME],
|
||||
dev->SMA + SMA_wCurrMastVolRight);
|
||||
update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
|
||||
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
|
||||
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
|
||||
|
||||
@@ -100,6 +100,16 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
|
||||
#define PFX "sc6000: "
|
||||
#define DRV_NAME "SC-6000"
|
||||
|
||||
struct snd_sc6000 {
|
||||
char __iomem *vport;
|
||||
char __iomem *vmss_port;
|
||||
struct snd_wss *chip;
|
||||
u8 mss_config;
|
||||
u8 config;
|
||||
u8 hw_cfg[2];
|
||||
bool old_dsp;
|
||||
};
|
||||
|
||||
/* hardware dependent functions */
|
||||
|
||||
/*
|
||||
@@ -267,7 +277,7 @@ static int sc6000_dsp_reset(char __iomem *vport)
|
||||
|
||||
/* detection and initialization */
|
||||
static int sc6000_hw_cfg_write(struct device *devptr,
|
||||
char __iomem *vport, const int *cfg)
|
||||
char __iomem *vport, const u8 *cfg)
|
||||
{
|
||||
if (sc6000_write(devptr, vport, COMMAND_6C) < 0) {
|
||||
dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C);
|
||||
@@ -353,8 +363,7 @@ static int sc6000_init_mss(struct device *devptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sc6000_hw_cfg_encode(struct device *devptr,
|
||||
char __iomem *vport, int *cfg,
|
||||
static void sc6000_hw_cfg_encode(struct device *devptr, u8 *cfg,
|
||||
long xport, long xmpu,
|
||||
long xmss_port, int joystick)
|
||||
{
|
||||
@@ -376,27 +385,83 @@ static void sc6000_hw_cfg_encode(struct device *devptr,
|
||||
dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]);
|
||||
}
|
||||
|
||||
static int sc6000_init_board(struct device *devptr,
|
||||
char __iomem *vport,
|
||||
char __iomem *vmss_port, int dev)
|
||||
static void sc6000_prepare_board(struct device *devptr,
|
||||
struct snd_sc6000 *sc6000,
|
||||
unsigned int dev, int xirq, int xdma)
|
||||
{
|
||||
sc6000->mss_config = sc6000_irq_to_softcfg(xirq) |
|
||||
sc6000_dma_to_softcfg(xdma);
|
||||
sc6000->config = sc6000->mss_config |
|
||||
sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
|
||||
sc6000_hw_cfg_encode(devptr, sc6000->hw_cfg, port[dev], mpu_port[dev],
|
||||
mss_port[dev], joystick[dev]);
|
||||
}
|
||||
|
||||
static void sc6000_detect_old_dsp(struct device *devptr,
|
||||
struct snd_sc6000 *sc6000)
|
||||
{
|
||||
sc6000_write(devptr, sc6000->vport, COMMAND_5C);
|
||||
sc6000->old_dsp = sc6000_read(sc6000->vport) < 0;
|
||||
}
|
||||
|
||||
static int sc6000_program_board(struct device *devptr,
|
||||
struct snd_sc6000 *sc6000)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!sc6000->old_dsp) {
|
||||
if (sc6000_hw_cfg_write(devptr, sc6000->vport,
|
||||
sc6000->hw_cfg) < 0) {
|
||||
dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sc6000_dsp_reset(sc6000->vport);
|
||||
|
||||
if (!sc6000->old_dsp) {
|
||||
sc6000_write(devptr, sc6000->vport, COMMAND_60);
|
||||
sc6000_write(devptr, sc6000->vport, 0x02);
|
||||
sc6000_dsp_reset(sc6000->vport);
|
||||
}
|
||||
|
||||
err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = sc6000_init_mss(devptr, sc6000->vport, sc6000->config,
|
||||
sc6000->vmss_port, sc6000->mss_config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc6000_init_board(struct device *devptr, struct snd_sc6000 *sc6000)
|
||||
{
|
||||
char answer[15];
|
||||
char version[2];
|
||||
int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
|
||||
sc6000_dma_to_softcfg(dma[dev]);
|
||||
int config = mss_config |
|
||||
sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
|
||||
int err;
|
||||
int old = 0;
|
||||
|
||||
err = sc6000_dsp_reset(vport);
|
||||
err = sc6000_dsp_reset(sc6000->vport);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_dsp_reset: failed!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(answer, 0, sizeof(answer));
|
||||
err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15);
|
||||
err = sc6000_dsp_get_answer(devptr, sc6000->vport, GET_DSP_COPYRIGHT,
|
||||
answer, 15);
|
||||
if (err <= 0) {
|
||||
dev_err(devptr, "sc6000_dsp_copyright: failed!\n");
|
||||
return -ENODEV;
|
||||
@@ -408,54 +473,17 @@ static int sc6000_init_board(struct device *devptr,
|
||||
if (strncmp("SC-6000", answer, 7))
|
||||
dev_warn(devptr, "Warning: non SC-6000 audio card!\n");
|
||||
|
||||
if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) {
|
||||
if (sc6000_dsp_get_answer(devptr, sc6000->vport,
|
||||
GET_DSP_VERSION, version, 2) < 2) {
|
||||
dev_err(devptr, "sc6000_dsp_version: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(devptr, "Detected model: %s, DSP version %d.%d\n",
|
||||
answer, version[0], version[1]);
|
||||
|
||||
/* set configuration */
|
||||
sc6000_write(devptr, vport, COMMAND_5C);
|
||||
if (sc6000_read(vport) < 0)
|
||||
old = 1;
|
||||
sc6000_detect_old_dsp(devptr, sc6000);
|
||||
|
||||
if (!old) {
|
||||
int cfg[2];
|
||||
sc6000_hw_cfg_encode(devptr,
|
||||
vport, &cfg[0], port[dev], mpu_port[dev],
|
||||
mss_port[dev], joystick[dev]);
|
||||
if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) {
|
||||
dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
err = sc6000_setup_board(devptr, vport, config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sc6000_dsp_reset(vport);
|
||||
|
||||
if (!old) {
|
||||
sc6000_write(devptr, vport, COMMAND_60);
|
||||
sc6000_write(devptr, vport, 0x02);
|
||||
sc6000_dsp_reset(vport);
|
||||
}
|
||||
|
||||
err = sc6000_setup_board(devptr, vport, config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return sc6000_program_board(devptr, sc6000);
|
||||
}
|
||||
|
||||
static int snd_sc6000_mixer(struct snd_wss *chip)
|
||||
@@ -538,10 +566,10 @@ static int snd_sc6000_match(struct device *devptr, unsigned int dev)
|
||||
|
||||
static void snd_sc6000_free(struct snd_card *card)
|
||||
{
|
||||
char __iomem *vport = (char __force __iomem *)card->private_data;
|
||||
struct snd_sc6000 *sc6000 = card->private_data;
|
||||
|
||||
if (vport)
|
||||
sc6000_setup_board(card->dev, vport, 0);
|
||||
if (sc6000->vport)
|
||||
sc6000_setup_board(card->dev, sc6000->vport, 0);
|
||||
}
|
||||
|
||||
static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
@@ -552,15 +580,17 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
int xirq = irq[dev];
|
||||
int xdma = dma[dev];
|
||||
struct snd_card *card;
|
||||
struct snd_sc6000 *sc6000;
|
||||
struct snd_wss *chip;
|
||||
struct snd_opl3 *opl3;
|
||||
char __iomem *vport;
|
||||
char __iomem *vmss_port;
|
||||
|
||||
err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
|
||||
0, &card);
|
||||
sizeof(*sc6000), &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
sc6000 = card->private_data;
|
||||
|
||||
if (xirq == SNDRV_AUTO_IRQ) {
|
||||
xirq = snd_legacy_find_free_irq(possible_irqs);
|
||||
@@ -587,7 +617,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
dev_err(devptr, "I/O port cannot be iomapped.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
card->private_data = (void __force *)vport;
|
||||
sc6000->vport = vport;
|
||||
|
||||
/* to make it marked as used */
|
||||
if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
|
||||
@@ -600,12 +630,15 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
dev_err(devptr, "MSS port I/O cannot be iomapped.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
sc6000->vmss_port = vmss_port;
|
||||
|
||||
dev_dbg(devptr, "Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
|
||||
port[dev], xirq, xdma,
|
||||
mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
|
||||
|
||||
err = sc6000_init_board(devptr, vport, vmss_port, dev);
|
||||
sc6000_prepare_board(devptr, sc6000, dev, xirq, xdma);
|
||||
|
||||
err = sc6000_init_board(devptr, sc6000);
|
||||
if (err < 0)
|
||||
return err;
|
||||
card->private_free = snd_sc6000_free;
|
||||
@@ -614,6 +647,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
WSS_HW_DETECT, 0, &chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
sc6000->chip = chip;
|
||||
|
||||
err = snd_wss_pcm(chip, 0);
|
||||
if (err < 0) {
|
||||
@@ -670,10 +704,47 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
||||
return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_sc6000_suspend(struct device *devptr, unsigned int dev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(devptr);
|
||||
struct snd_sc6000 *sc6000 = card->private_data;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
sc6000->chip->suspend(sc6000->chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_sc6000_resume(struct device *devptr, unsigned int dev)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(devptr);
|
||||
struct snd_sc6000 *sc6000 = card->private_data;
|
||||
int err;
|
||||
|
||||
err = sc6000_dsp_reset(sc6000->vport);
|
||||
if (err < 0) {
|
||||
dev_err(devptr, "sc6000_dsp_reset: failed!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = sc6000_program_board(devptr, sc6000);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sc6000->chip->resume(sc6000->chip);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct isa_driver snd_sc6000_driver = {
|
||||
.match = snd_sc6000_match,
|
||||
.probe = snd_sc6000_probe,
|
||||
/* FIXME: suspend/resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_sc6000_suspend,
|
||||
.resume = snd_sc6000_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
|
||||
@@ -131,6 +131,11 @@ enum card_type {
|
||||
struct soundscape {
|
||||
spinlock_t lock;
|
||||
unsigned io_base;
|
||||
unsigned long wss_base;
|
||||
int irq;
|
||||
int mpu_irq;
|
||||
int dma1;
|
||||
int dma2;
|
||||
int ic_type;
|
||||
enum card_type type;
|
||||
struct resource *io_res;
|
||||
@@ -138,6 +143,8 @@ struct soundscape {
|
||||
struct snd_wss *chip;
|
||||
|
||||
unsigned char midi_vol;
|
||||
bool joystick;
|
||||
bool midi_enabled;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
@@ -149,6 +156,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
|
||||
return (struct soundscape *) (c->private_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the resolved board settings in the per-card state so that
|
||||
* the same configuration can be replayed later if necessary.
|
||||
*/
|
||||
static void sscape_store_settings(struct soundscape *sscape, int dev)
|
||||
{
|
||||
sscape->io_base = port[dev];
|
||||
sscape->wss_base = wss_port[dev];
|
||||
sscape->irq = irq[dev];
|
||||
sscape->mpu_irq = mpu_irq[dev];
|
||||
sscape->dma1 = dma[dev];
|
||||
sscape->dma2 = dma2[dev];
|
||||
sscape->joystick = joystick[dev];
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates some kernel memory that we can use for DMA.
|
||||
* I think this means that the memory has to map to
|
||||
@@ -263,34 +285,36 @@ static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout)
|
||||
|
||||
/*
|
||||
* Write to the SoundScape's host-mode control registers, but
|
||||
* leave any locking issues to the caller ...
|
||||
* leave any locking issues to the caller. Returns true if
|
||||
* the write succeeded.
|
||||
*/
|
||||
static inline int host_write_unsafe(unsigned io_base, unsigned char data)
|
||||
static inline bool host_write_unsafe(unsigned int io_base, unsigned char data)
|
||||
{
|
||||
if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {
|
||||
outb(data, HOST_DATA_IO(io_base));
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the SoundScape's host-mode control registers, performing
|
||||
* a limited amount of busy-waiting if the register isn't ready.
|
||||
* Also leaves all locking-issues to the caller ...
|
||||
* Also leaves all locking-issues to the caller. Returns true if
|
||||
* the write succeeded before timing out.
|
||||
*/
|
||||
static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
|
||||
unsigned timeout)
|
||||
static bool host_write_ctrl_unsafe(unsigned int io_base, unsigned char data,
|
||||
unsigned int timeout)
|
||||
{
|
||||
int err;
|
||||
bool written;
|
||||
|
||||
while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {
|
||||
while (!(written = host_write_unsafe(io_base, data)) && timeout != 0) {
|
||||
udelay(100);
|
||||
--timeout;
|
||||
} /* while */
|
||||
|
||||
return err;
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
@@ -560,6 +584,30 @@ static int sscape_upload_microcode(struct snd_card *card, int version)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the SoundScape's MIDI control state after the firmware
|
||||
* upload has made the host interface available again.
|
||||
*/
|
||||
static int sscape_restore_midi_state(struct soundscape *sscape)
|
||||
{
|
||||
bool success;
|
||||
|
||||
guard(spinlock_irqsave)(&sscape->lock);
|
||||
set_host_mode_unsafe(sscape->io_base);
|
||||
|
||||
success = host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, CMD_SET_EXTMIDI, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, 0, 100) &&
|
||||
host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
|
||||
|
||||
set_midi_mode_unsafe(sscape->io_base);
|
||||
|
||||
return success ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mixer control for the SoundScape's MIDI device.
|
||||
*/
|
||||
@@ -660,6 +708,59 @@ static unsigned get_irq_config(int sscape_type, int irq)
|
||||
return INVALID_IRQ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Program the SoundScape's board-specific routing and enable the
|
||||
* codec path using the resolved IRQ, DMA and joystick settings.
|
||||
*/
|
||||
static int sscape_configure_board(struct soundscape *sscape)
|
||||
{
|
||||
unsigned int dma_cfg;
|
||||
unsigned int irq_cfg;
|
||||
unsigned int mpu_irq_cfg;
|
||||
int val;
|
||||
|
||||
irq_cfg = get_irq_config(sscape->type, sscape->irq);
|
||||
if (irq_cfg == INVALID_IRQ)
|
||||
return -ENXIO;
|
||||
|
||||
mpu_irq_cfg = get_irq_config(sscape->type, sscape->mpu_irq);
|
||||
if (mpu_irq_cfg == INVALID_IRQ)
|
||||
return -ENXIO;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &sscape->lock) {
|
||||
if (sscape->ic_type == IC_OPUS)
|
||||
activate_ad1845_unsafe(sscape->io_base);
|
||||
|
||||
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
|
||||
sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
|
||||
|
||||
/*
|
||||
* Enable and configure the DMA channels ...
|
||||
*/
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
|
||||
dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
|
||||
|
||||
mpu_irq_cfg |= mpu_irq_cfg << 2;
|
||||
val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xf7;
|
||||
if (sscape->joystick)
|
||||
val |= 0x08;
|
||||
sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0xd0);
|
||||
sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG,
|
||||
0xf0 | mpu_irq_cfg);
|
||||
sscape_write_unsafe(sscape->io_base, GA_CDCFG_REG,
|
||||
0x09 | DMA_8BIT |
|
||||
(sscape->dma1 << 4) | (irq_cfg << 1));
|
||||
/*
|
||||
* Enable the master IRQ ...
|
||||
*/
|
||||
sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform certain arcane port-checks to see whether there
|
||||
* is a SoundScape board lurking behind the given ports.
|
||||
@@ -890,37 +991,33 @@ static int create_ad1845(struct snd_card *card, unsigned port,
|
||||
|
||||
/*
|
||||
* Create an ALSA soundcard entry for the SoundScape, using
|
||||
* the given list of port, IRQ and DMA resources.
|
||||
* the resolved port, IRQ and DMA resources.
|
||||
*/
|
||||
static int create_sscape(int dev, struct snd_card *card)
|
||||
static int create_sscape(struct snd_card *card)
|
||||
{
|
||||
struct soundscape *sscape = get_card_soundscape(card);
|
||||
unsigned dma_cfg;
|
||||
unsigned irq_cfg;
|
||||
unsigned mpu_irq_cfg;
|
||||
struct resource *io_res;
|
||||
struct resource *wss_res;
|
||||
int err;
|
||||
int val;
|
||||
const char *name;
|
||||
|
||||
/*
|
||||
* Grab IO ports that we will need to probe so that we
|
||||
* can detect and control this hardware ...
|
||||
*/
|
||||
io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape");
|
||||
io_res = devm_request_region(card->dev, sscape->io_base, 8, "SoundScape");
|
||||
if (!io_res) {
|
||||
dev_err(card->dev,
|
||||
"sscape: can't grab port 0x%lx\n", port[dev]);
|
||||
"sscape: can't grab port 0x%x\n", sscape->io_base);
|
||||
return -EBUSY;
|
||||
}
|
||||
wss_res = NULL;
|
||||
if (sscape->type == SSCAPE_VIVO) {
|
||||
wss_res = devm_request_region(card->dev, wss_port[dev], 4,
|
||||
wss_res = devm_request_region(card->dev, sscape->wss_base, 4,
|
||||
"SoundScape");
|
||||
if (!wss_res) {
|
||||
dev_err(card->dev, "sscape: can't grab port 0x%lx\n",
|
||||
wss_port[dev]);
|
||||
sscape->wss_base);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
@@ -928,18 +1025,17 @@ static int create_sscape(int dev, struct snd_card *card)
|
||||
/*
|
||||
* Grab one DMA channel ...
|
||||
*/
|
||||
err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape");
|
||||
err = snd_devm_request_dma(card->dev, sscape->dma1, "SoundScape");
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]);
|
||||
dev_err(card->dev, "sscape: can't grab DMA %d\n", sscape->dma1);
|
||||
return err;
|
||||
}
|
||||
|
||||
spin_lock_init(&sscape->lock);
|
||||
sscape->io_res = io_res;
|
||||
sscape->wss_res = wss_res;
|
||||
sscape->io_base = port[dev];
|
||||
|
||||
if (!detect_sscape(sscape, wss_port[dev])) {
|
||||
if (!detect_sscape(sscape, sscape->wss_base)) {
|
||||
dev_err(card->dev, "sscape: hardware not detected at 0x%x\n",
|
||||
sscape->io_base);
|
||||
return -ENODEV;
|
||||
@@ -964,66 +1060,28 @@ static int create_sscape(int dev, struct snd_card *card)
|
||||
}
|
||||
|
||||
dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
|
||||
name, sscape->io_base, irq[dev], dma[dev]);
|
||||
|
||||
/*
|
||||
* Check that the user didn't pass us garbage data ...
|
||||
*/
|
||||
irq_cfg = get_irq_config(sscape->type, irq[dev]);
|
||||
if (irq_cfg == INVALID_IRQ) {
|
||||
dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
|
||||
if (mpu_irq_cfg == INVALID_IRQ) {
|
||||
dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
|
||||
return -ENXIO;
|
||||
}
|
||||
name, sscape->io_base, sscape->irq, sscape->dma1);
|
||||
|
||||
/*
|
||||
* Tell the on-board devices where their resources are (I think -
|
||||
* I can't be sure without a datasheet ... So many magic values!)
|
||||
*/
|
||||
scoped_guard(spinlock_irqsave, &sscape->lock) {
|
||||
|
||||
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
|
||||
sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
|
||||
|
||||
/*
|
||||
* Enable and configure the DMA channels ...
|
||||
*/
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
|
||||
dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
|
||||
sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
|
||||
|
||||
mpu_irq_cfg |= mpu_irq_cfg << 2;
|
||||
val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
|
||||
if (joystick[dev])
|
||||
val |= 8;
|
||||
sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
|
||||
sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
|
||||
sscape_write_unsafe(sscape->io_base,
|
||||
GA_CDCFG_REG, 0x09 | DMA_8BIT
|
||||
| (dma[dev] << 4) | (irq_cfg << 1));
|
||||
/*
|
||||
* Enable the master IRQ ...
|
||||
*/
|
||||
sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
|
||||
|
||||
err = sscape_configure_board(sscape);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "sscape: Invalid IRQ configuration\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have now enabled the codec chip, and so we should
|
||||
* detect the AD1845 device ...
|
||||
*/
|
||||
err = create_ad1845(card, wss_port[dev], irq[dev],
|
||||
dma[dev], dma2[dev]);
|
||||
err = create_ad1845(card, sscape->wss_base, sscape->irq,
|
||||
sscape->dma1, sscape->dma2);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev,
|
||||
"sscape: No AD1845 device at 0x%lx, IRQ %d\n",
|
||||
wss_port[dev], irq[dev]);
|
||||
sscape->wss_base, sscape->irq);
|
||||
return err;
|
||||
}
|
||||
strscpy(card->driver, "SoundScape");
|
||||
@@ -1040,41 +1098,99 @@ static int create_sscape(int dev, struct snd_card *card)
|
||||
err = sscape_upload_microcode(card, err);
|
||||
|
||||
if (err == 0) {
|
||||
err = create_mpu401(card, MIDI_DEVNUM, port[dev],
|
||||
mpu_irq[dev]);
|
||||
err = create_mpu401(card, MIDI_DEVNUM, sscape->io_base,
|
||||
sscape->mpu_irq);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev,
|
||||
"sscape: Failed to create MPU-401 device at 0x%lx\n",
|
||||
port[dev]);
|
||||
(unsigned long)sscape->io_base);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize mixer
|
||||
*/
|
||||
guard(spinlock_irqsave)(&sscape->lock);
|
||||
sscape->midi_vol = 0;
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
CMD_SET_MIDI_VOL, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
sscape->midi_vol, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
CMD_XXX_MIDI_VOL, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
sscape->midi_vol, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
CMD_SET_EXTMIDI, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base,
|
||||
0, 100);
|
||||
host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
|
||||
|
||||
set_midi_mode_unsafe(sscape->io_base);
|
||||
sscape->midi_enabled = true;
|
||||
err = sscape_restore_midi_state(sscape);
|
||||
if (err < 0)
|
||||
dev_warn(card->dev,
|
||||
"sscape: MIDI init incomplete: %d\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* Reload the MIDI firmware and restore the saved MIDI state for
|
||||
* boards whose MPU-401 side was enabled during probe.
|
||||
*/
|
||||
static int sscape_resume_midi(struct snd_card *card)
|
||||
{
|
||||
struct soundscape *sscape = get_card_soundscape(card);
|
||||
int err, version;
|
||||
|
||||
if (!sscape->midi_enabled)
|
||||
return 0;
|
||||
|
||||
version = sscape_upload_bootblock(card);
|
||||
if (version < 0)
|
||||
return version;
|
||||
|
||||
err = sscape_upload_microcode(card, version);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
outb(0, sscape->io_base);
|
||||
|
||||
return sscape_restore_midi_state(sscape);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the WSS codec state before the SoundScape is suspended.
|
||||
*/
|
||||
static int snd_sscape_suspend_card(struct snd_card *card)
|
||||
{
|
||||
struct soundscape *sscape = get_card_soundscape(card);
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
sscape->chip->suspend(sscape->chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the board-specific state before resuming the WSS codec.
|
||||
*/
|
||||
static int snd_sscape_resume_card(struct snd_card *card)
|
||||
{
|
||||
struct soundscape *sscape = get_card_soundscape(card);
|
||||
int err;
|
||||
|
||||
err = sscape_configure_board(sscape);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = sscape_resume_midi(card);
|
||||
if (err < 0)
|
||||
dev_warn(card->dev, "sscape: MIDI restore failed: %d\n", err);
|
||||
|
||||
sscape->chip->resume(sscape->chip);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_sscape_suspend(struct device *dev, unsigned int n,
|
||||
pm_message_t state)
|
||||
{
|
||||
return snd_sscape_suspend_card(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int snd_sscape_resume(struct device *dev, unsigned int n)
|
||||
{
|
||||
return snd_sscape_resume_card(dev_get_drvdata(dev));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int snd_sscape_match(struct device *pdev, unsigned int i)
|
||||
{
|
||||
@@ -1111,8 +1227,9 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
|
||||
sscape->type = SSCAPE;
|
||||
|
||||
dma[dev] &= 0x03;
|
||||
sscape_store_settings(sscape, dev);
|
||||
|
||||
ret = create_sscape(dev, card);
|
||||
ret = create_sscape(card);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1130,7 +1247,10 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
|
||||
static struct isa_driver snd_sscape_driver = {
|
||||
.match = snd_sscape_match,
|
||||
.probe = snd_sscape_probe,
|
||||
/* FIXME: suspend/resume */
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_sscape_suspend,
|
||||
.resume = snd_sscape_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = DEV_NAME
|
||||
},
|
||||
@@ -1211,8 +1331,9 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
|
||||
wss_port[idx] = pnp_port_start(dev, 1);
|
||||
dma2[idx] = pnp_dma(dev, 1);
|
||||
}
|
||||
sscape_store_settings(sscape, idx);
|
||||
|
||||
ret = create_sscape(idx, card);
|
||||
ret = create_sscape(card);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1227,11 +1348,27 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sscape_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
|
||||
{
|
||||
return snd_sscape_suspend_card(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
|
||||
static int sscape_pnp_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
return snd_sscape_resume_card(pnp_get_card_drvdata(pcard));
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pnp_card_driver sscape_pnpc_driver = {
|
||||
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
|
||||
.name = "sscape",
|
||||
.id_table = sscape_pnpids,
|
||||
.probe = sscape_pnp_detect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = sscape_pnp_suspend,
|
||||
.resume = sscape_pnp_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* CONFIG_PNP */
|
||||
|
||||
@@ -1362,6 +1362,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
|
||||
struct hpi_control *hpi_ctl,
|
||||
char *name)
|
||||
{
|
||||
int len;
|
||||
char *dir;
|
||||
memset(snd_control, 0, sizeof(*snd_control));
|
||||
snd_control->name = hpi_ctl->name;
|
||||
@@ -1384,23 +1385,30 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
|
||||
dir = "Playback "; /* PCM Playback source, or output node */
|
||||
|
||||
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
|
||||
sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
|
||||
asihpi_src_names[hpi_ctl->src_node_type],
|
||||
hpi_ctl->src_node_index,
|
||||
asihpi_dst_names[hpi_ctl->dst_node_type],
|
||||
hpi_ctl->dst_node_index,
|
||||
dir, name);
|
||||
len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name),
|
||||
"%s %d %s %d %s%s",
|
||||
asihpi_src_names[hpi_ctl->src_node_type],
|
||||
hpi_ctl->src_node_index,
|
||||
asihpi_dst_names[hpi_ctl->dst_node_type],
|
||||
hpi_ctl->dst_node_index,
|
||||
dir, name);
|
||||
else if (hpi_ctl->dst_node_type) {
|
||||
sprintf(hpi_ctl->name, "%s %d %s%s",
|
||||
asihpi_dst_names[hpi_ctl->dst_node_type],
|
||||
hpi_ctl->dst_node_index,
|
||||
dir, name);
|
||||
len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name),
|
||||
"%s %d %s%s",
|
||||
asihpi_dst_names[hpi_ctl->dst_node_type],
|
||||
hpi_ctl->dst_node_index,
|
||||
dir, name);
|
||||
} else {
|
||||
sprintf(hpi_ctl->name, "%s %d %s%s",
|
||||
asihpi_src_names[hpi_ctl->src_node_type],
|
||||
hpi_ctl->src_node_index,
|
||||
dir, name);
|
||||
len = snprintf(hpi_ctl->name, sizeof(hpi_ctl->name),
|
||||
"%s %d %s%s",
|
||||
asihpi_src_names[hpi_ctl->src_node_type],
|
||||
hpi_ctl->src_node_index,
|
||||
dir, name);
|
||||
}
|
||||
|
||||
if (len >= sizeof(hpi_ctl->name))
|
||||
pr_err("asihpi: truncated control name: %s\n",
|
||||
hpi_ctl->name);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------
|
||||
|
||||
@@ -794,7 +794,8 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
|
||||
struct src *src;
|
||||
int err;
|
||||
int n_amixer = apcm->substream->runtime->channels, i;
|
||||
unsigned int pitch, rsr = atc->pll_rate;
|
||||
unsigned int pitch;
|
||||
unsigned int rsr = atc->pll_rate ? atc->pll_rate : atc->rsr;
|
||||
|
||||
/* first release old resources */
|
||||
atc_pcm_release_resources(atc, apcm);
|
||||
@@ -983,6 +984,11 @@ static int atc_select_mic_in(struct ct_atc *atc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline enum DAIOTYP atc_spdif_in_type(struct ct_atc *atc)
|
||||
{
|
||||
return (atc->model == CTSB073X) ? SPDIFI_BAY : SPDIFIO;
|
||||
}
|
||||
|
||||
static struct capabilities atc_capabilities(struct ct_atc *atc)
|
||||
{
|
||||
struct hw *hw = atc->hw;
|
||||
@@ -1121,7 +1127,7 @@ static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
|
||||
|
||||
static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state)
|
||||
{
|
||||
return atc_daio_unmute(atc, state, SPDIFIO);
|
||||
return atc_daio_unmute(atc, state, atc_spdif_in_type(atc));
|
||||
}
|
||||
|
||||
static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status)
|
||||
@@ -1404,9 +1410,11 @@ static int atc_get_resources(struct ct_atc *atc)
|
||||
struct sum_desc sum_dsc = {0};
|
||||
struct sum_mgr *sum_mgr;
|
||||
struct capabilities cap;
|
||||
int atc_srcs_limit;
|
||||
int err, i;
|
||||
|
||||
cap = atc->capabilities(atc);
|
||||
atc_srcs_limit = cap.dedicated_mic ? NUM_ATC_SRCS : 4;
|
||||
|
||||
atc->daios = kcalloc(NUM_DAIOTYP, sizeof(void *), GFP_KERNEL);
|
||||
if (!atc->daios)
|
||||
@@ -1427,14 +1435,12 @@ static int atc_get_resources(struct ct_atc *atc)
|
||||
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
|
||||
da_desc.msr = atc->msr;
|
||||
for (i = 0; i < NUM_DAIOTYP; i++) {
|
||||
if (((i == MIC) && !cap.dedicated_mic) ||
|
||||
((i == RCA) && !cap.dedicated_rca) ||
|
||||
i == SPDIFI1)
|
||||
if (((i == SPDIFIO) && (atc->model == CTSB073X)) ||
|
||||
((i == SPDIFI_BAY) && (atc->model != CTSB073X)) ||
|
||||
((i == MIC) && !cap.dedicated_mic) ||
|
||||
((i == RCA) && !cap.dedicated_rca))
|
||||
continue;
|
||||
if (atc->model == CTSB073X && i == SPDIFIO)
|
||||
da_desc.type = SPDIFI1;
|
||||
else
|
||||
da_desc.type = i;
|
||||
da_desc.type = i;
|
||||
da_desc.output = (i < LINEIM) || (i == RCA);
|
||||
err = daio_mgr->get_daio(daio_mgr, &da_desc,
|
||||
(struct daio **)&atc->daios[i]);
|
||||
@@ -1450,9 +1456,7 @@ static int atc_get_resources(struct ct_atc *atc)
|
||||
src_dsc.multi = 1;
|
||||
src_dsc.msr = atc->msr;
|
||||
src_dsc.mode = ARCRW;
|
||||
for (i = 0; i < NUM_ATC_SRCS; i++) {
|
||||
if (((i > 3) && !cap.dedicated_mic))
|
||||
continue;
|
||||
for (i = 0; i < atc_srcs_limit; i++) {
|
||||
err = src_mgr->get_src(src_mgr, &src_dsc,
|
||||
(struct src **)&atc->srcs[i]);
|
||||
if (err)
|
||||
@@ -1461,9 +1465,7 @@ static int atc_get_resources(struct ct_atc *atc)
|
||||
|
||||
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
|
||||
srcimp_dsc.msr = 8;
|
||||
for (i = 0; i < NUM_ATC_SRCS; i++) {
|
||||
if (((i > 3) && !cap.dedicated_mic))
|
||||
continue;
|
||||
for (i = 0; i < atc_srcs_limit; i++) {
|
||||
err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
|
||||
(struct srcimp **)&atc->srcimps[i]);
|
||||
if (err)
|
||||
@@ -1569,7 +1571,7 @@ static void atc_connect_resources(struct ct_atc *atc)
|
||||
mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
|
||||
}
|
||||
|
||||
dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
|
||||
dai = container_of(atc->daios[atc_spdif_in_type(atc)], struct dai, daio);
|
||||
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
|
||||
(struct src **)&atc->srcs[0],
|
||||
(struct srcimp **)&atc->srcimps[0]);
|
||||
|
||||
@@ -35,7 +35,7 @@ static const struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
|
||||
[LINEIM] = {.left = 0x1b5, .right = 0x1bd},
|
||||
[SPDIFOO] = {.left = 0x20, .right = 0x21},
|
||||
[SPDIFIO] = {.left = 0x15, .right = 0x1d},
|
||||
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
|
||||
[SPDIFI_BAY] = {.left = 0x95, .right = 0x9d},
|
||||
};
|
||||
|
||||
static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
|
||||
@@ -106,7 +106,7 @@ static int daio_device_index(enum DAIOTYP type, struct hw *hw)
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case SPDIFI1: return 1;
|
||||
case SPDIFI_BAY: return 1;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
@@ -120,7 +120,6 @@ static int daio_device_index(enum DAIOTYP type, struct hw *hw)
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case SPDIFI1: return 1;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
|
||||
@@ -32,7 +32,7 @@ enum DAIOTYP {
|
||||
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
|
||||
MIC, /* Dedicated mic on Titanium HD */
|
||||
RCA, /* Dedicated RCA on SE-300PCIE */
|
||||
SPDIFI1, /* S/PDIF In on internal Drive Bay */
|
||||
SPDIFI_BAY, /* S/PDIF In on internal drive bay */
|
||||
NUM_DAIOTYP
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#ifndef CTVMEM_H
|
||||
#define CTVMEM_H
|
||||
|
||||
#define CT_PTP_NUM 4 /* num of device page table pages */
|
||||
#define CT_PTP_NUM 1 /* num of device page table pages */
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
@@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
|
||||
usb6fire_comm_abort(chip);
|
||||
if (chip->control)
|
||||
usb6fire_control_abort(chip);
|
||||
if (chip->card) {
|
||||
snd_card_disconnect(chip->card);
|
||||
snd_card_free_when_closed(chip->card);
|
||||
chip->card = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +163,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
|
||||
static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct sfire_chip *chip;
|
||||
struct snd_card *card;
|
||||
|
||||
chip = usb_get_intfdata(intf);
|
||||
if (chip) { /* if !chip, fw upload has been performed */
|
||||
@@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
chips[chip->regidx] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save card pointer before teardown.
|
||||
* snd_card_free_when_closed() may free card (and
|
||||
* the embedded chip) immediately, so it must be
|
||||
* called last and chip must not be accessed after.
|
||||
*/
|
||||
card = chip->card;
|
||||
chip->shutdown = true;
|
||||
if (card)
|
||||
snd_card_disconnect(card);
|
||||
usb6fire_chip_abort(chip);
|
||||
if (card)
|
||||
snd_card_free_when_closed(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,9 +631,9 @@ static void usb_audio_make_shortname(struct usb_device *dev,
|
||||
}
|
||||
|
||||
/* retrieve the device string as shortname */
|
||||
if (!dev->descriptor.iProduct ||
|
||||
usb_string(dev, dev->descriptor.iProduct,
|
||||
card->shortname, sizeof(card->shortname)) <= 0) {
|
||||
if (dev->product && *dev->product) {
|
||||
strscpy(card->shortname, dev->product);
|
||||
} else {
|
||||
/* no name available from anywhere, so use ID */
|
||||
scnprintf(card->shortname, sizeof(card->shortname),
|
||||
"USB Device %#04x:%#04x",
|
||||
@@ -668,15 +668,11 @@ static void usb_audio_make_longname(struct usb_device *dev,
|
||||
else if (quirk && quirk->vendor_name)
|
||||
s = quirk->vendor_name;
|
||||
*card->longname = 0;
|
||||
if (s && *s) {
|
||||
strscpy(card->longname, s, sizeof(card->longname));
|
||||
} else {
|
||||
/* retrieve the vendor and device strings as longname */
|
||||
if (dev->descriptor.iManufacturer)
|
||||
usb_string(dev, dev->descriptor.iManufacturer,
|
||||
card->longname, sizeof(card->longname));
|
||||
/* we don't really care if there isn't any vendor string */
|
||||
}
|
||||
if (s && *s)
|
||||
strscpy(card->longname, s);
|
||||
else if (dev->manufacturer && *dev->manufacturer)
|
||||
strscpy(card->longname, dev->manufacturer);
|
||||
|
||||
if (*card->longname) {
|
||||
strim(card->longname);
|
||||
if (*card->longname)
|
||||
@@ -870,19 +866,25 @@ static void find_last_interface(struct snd_usb_audio *chip)
|
||||
|
||||
/* look for the corresponding quirk */
|
||||
static const struct snd_usb_audio_quirk *
|
||||
get_alias_quirk(struct usb_device *dev, unsigned int id)
|
||||
get_alias_quirk(struct usb_interface *intf, unsigned int id)
|
||||
{
|
||||
const struct usb_device_id *p;
|
||||
struct usb_device_id match_id;
|
||||
|
||||
for (p = usb_audio_ids; p->match_flags; p++) {
|
||||
/* FIXME: this checks only vendor:product pair in the list */
|
||||
if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
|
||||
USB_DEVICE_ID_MATCH_DEVICE &&
|
||||
p->idVendor == USB_ID_VENDOR(id) &&
|
||||
p->idProduct == USB_ID_PRODUCT(id))
|
||||
return (const struct snd_usb_audio_quirk *)p->driver_info;
|
||||
}
|
||||
if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) !=
|
||||
USB_DEVICE_ID_MATCH_DEVICE)
|
||||
continue;
|
||||
if (p->idVendor != USB_ID_VENDOR(id) ||
|
||||
p->idProduct != USB_ID_PRODUCT(id))
|
||||
continue;
|
||||
|
||||
match_id = *p;
|
||||
match_id.match_flags &= ~USB_DEVICE_ID_MATCH_DEVICE;
|
||||
if (!match_id.match_flags || usb_match_one_id(intf, &match_id))
|
||||
return (const struct snd_usb_audio_quirk *)
|
||||
p->driver_info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -931,7 +933,7 @@ static int usb_audio_probe(struct usb_interface *intf,
|
||||
id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
if (get_alias_id(dev, &id))
|
||||
quirk = get_alias_quirk(dev, id);
|
||||
quirk = get_alias_quirk(intf, id);
|
||||
if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
|
||||
return -ENXIO;
|
||||
if (quirk && quirk->ifnum == QUIRK_NODEV_INTERFACE)
|
||||
|
||||
@@ -455,6 +455,10 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
|
||||
if (chip->usb_id == USB_ID(0x194f, 0x010d) &&
|
||||
!s1810c_valid_sample_rate(fp, rate))
|
||||
goto skip_rate;
|
||||
/* Filter out invalid rates on Presonus Studio 1824 */
|
||||
if (chip->usb_id == USB_ID(0x194f, 0x0107) &&
|
||||
!s1810c_valid_sample_rate(fp, rate))
|
||||
goto skip_rate;
|
||||
|
||||
/* Filter out invalid rates on Focusrite devices */
|
||||
if (USB_ID_VENDOR(chip->usb_id) == 0x1235 &&
|
||||
|
||||
@@ -699,15 +699,18 @@ static void snd_usbmidi_transmit_byte(struct usbmidi_out_port *port,
|
||||
static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint *ep,
|
||||
struct urb *urb)
|
||||
{
|
||||
int p;
|
||||
int port0 = ep->current_port;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 0x10; ++i) {
|
||||
int portnum = (port0 + i) & 15;
|
||||
struct usbmidi_out_port *port = &ep->ports[portnum];
|
||||
|
||||
/* FIXME: lower-numbered ports can starve higher-numbered ports */
|
||||
for (p = 0; p < 0x10; ++p) {
|
||||
struct usbmidi_out_port *port = &ep->ports[p];
|
||||
if (!port->active)
|
||||
continue;
|
||||
while (urb->transfer_buffer_length + 3 < ep->max_transfer) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(port->substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
@@ -715,6 +718,7 @@ static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint *ep,
|
||||
snd_usbmidi_transmit_byte(port, b, urb);
|
||||
}
|
||||
}
|
||||
ep->current_port = (port0 + 1) & 15;
|
||||
}
|
||||
|
||||
static const struct usb_protocol_ops snd_usbmidi_standard_ops = {
|
||||
|
||||
@@ -1057,10 +1057,8 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi)
|
||||
strscpy(ump->core.name, ump->info.name,
|
||||
sizeof(ump->core.name));
|
||||
/* use serial number string as unique UMP product id */
|
||||
if (!*ump->info.product_id && dev->descriptor.iSerialNumber)
|
||||
usb_string(dev, dev->descriptor.iSerialNumber,
|
||||
ump->info.product_id,
|
||||
sizeof(ump->info.product_id));
|
||||
if (!*ump->info.product_id && dev->serial && *dev->serial)
|
||||
strscpy(ump->info.product_id, dev->serial);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1204,6 +1204,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
|
||||
cval->min = -11264; /* Mute under it */
|
||||
}
|
||||
break;
|
||||
case USB_ID(0x31b2, 0x0111): /* MOONDROP JU Jiu */
|
||||
if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
|
||||
usb_audio_info(chip,
|
||||
"set volume quirk for MOONDROP JU Jiu\n");
|
||||
cval->min = -10880; /* Mute under it */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,13 +1232,80 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
|
||||
snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional checks for sticky mixers
|
||||
*
|
||||
* Some devices' volume control mixers are sticky, which accept SET_CUR but
|
||||
* do absolutely nothing.
|
||||
*
|
||||
* Prevent sticky mixers from being registered, otherwise they confuses
|
||||
* userspace and results in ineffective volume control.
|
||||
*/
|
||||
static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
|
||||
int channel, int saved)
|
||||
{
|
||||
int sticky_test_values[] = { cval->min, cval->max };
|
||||
int test, check, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sticky_test_values); i++) {
|
||||
test = sticky_test_values[i];
|
||||
if (test == saved)
|
||||
continue;
|
||||
|
||||
/* Assume non-sticky on failure. */
|
||||
if (snd_usb_set_cur_mix_value(cval, channel, 0, test) ||
|
||||
get_cur_mix_raw(cval, channel, &check) ||
|
||||
check != saved) /* SET_CUR effective, non-sticky. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
usb_audio_err(cval->head.mixer->chip,
|
||||
"%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n",
|
||||
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
|
||||
cval->min, cval->max, cval->res, saved);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional checks for the proper resolution
|
||||
*
|
||||
* Some devices report smaller resolutions than actually reacting.
|
||||
* They don't return errors but simply clip to the lower aligned value.
|
||||
*/
|
||||
static void check_volume_control_res(struct usb_mixer_elem_info *cval,
|
||||
int channel, int saved)
|
||||
{
|
||||
int last_valid_res = cval->res;
|
||||
int test, check;
|
||||
|
||||
for (;;) {
|
||||
test = saved;
|
||||
if (test < cval->max)
|
||||
test += cval->res;
|
||||
else
|
||||
test -= cval->res;
|
||||
|
||||
if (test < cval->min || test > cval->max ||
|
||||
snd_usb_set_cur_mix_value(cval, channel, 0, test) ||
|
||||
get_cur_mix_raw(cval, channel, &check)) {
|
||||
cval->res = last_valid_res;
|
||||
break;
|
||||
}
|
||||
if (test == check)
|
||||
break;
|
||||
|
||||
cval->res *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* retrieve the minimum and maximum values for the specified control
|
||||
*/
|
||||
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
|
||||
int default_min, struct snd_kcontrol *kctl)
|
||||
{
|
||||
int i, idx;
|
||||
int i, idx, ret;
|
||||
|
||||
/* for failsafe */
|
||||
cval->min = default_min;
|
||||
@@ -1257,7 +1331,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
|
||||
"%d:%d: cannot get min/max values for control %d (id %d)\n",
|
||||
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
|
||||
cval->control, cval->head.id);
|
||||
return -EINVAL;
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (get_ctl_value(cval, UAC_GET_RES,
|
||||
(cval->control << 8) | minchn,
|
||||
@@ -1280,37 +1354,25 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
|
||||
if (cval->res == 0)
|
||||
cval->res = 1;
|
||||
|
||||
/* Additional checks for the proper resolution
|
||||
*
|
||||
* Some devices report smaller resolutions than actually
|
||||
* reacting. They don't return errors but simply clip
|
||||
* to the lower aligned value.
|
||||
*/
|
||||
if (cval->min + cval->res < cval->max) {
|
||||
int last_valid_res = cval->res;
|
||||
int saved, test, check;
|
||||
if (cval->min < cval->max) {
|
||||
int saved;
|
||||
|
||||
if (get_cur_mix_raw(cval, minchn, &saved) < 0)
|
||||
goto no_res_check;
|
||||
for (;;) {
|
||||
test = saved;
|
||||
if (test < cval->max)
|
||||
test += cval->res;
|
||||
else
|
||||
test -= cval->res;
|
||||
if (test < cval->min || test > cval->max ||
|
||||
snd_usb_set_cur_mix_value(cval, minchn, 0, test) ||
|
||||
get_cur_mix_raw(cval, minchn, &check)) {
|
||||
cval->res = last_valid_res;
|
||||
break;
|
||||
}
|
||||
if (test == check)
|
||||
break;
|
||||
cval->res *= 2;
|
||||
goto no_checks;
|
||||
|
||||
ret = check_sticky_volume_control(cval, minchn, saved);
|
||||
if (ret < 0) {
|
||||
snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cval->min + cval->res < cval->max)
|
||||
check_volume_control_res(cval, minchn, saved);
|
||||
|
||||
snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
|
||||
}
|
||||
|
||||
no_res_check:
|
||||
no_checks:
|
||||
cval->initialized = 1;
|
||||
}
|
||||
|
||||
@@ -1381,6 +1443,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
|
||||
int ret;
|
||||
|
||||
if (cval->val_type == USB_MIXER_BOOLEAN ||
|
||||
cval->val_type == USB_MIXER_INV_BOOLEAN)
|
||||
@@ -1391,8 +1454,9 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
|
||||
if (cval->val_type != USB_MIXER_BOOLEAN &&
|
||||
cval->val_type != USB_MIXER_INV_BOOLEAN) {
|
||||
if (!cval->initialized) {
|
||||
get_min_max_with_quirks(cval, 0, kcontrol);
|
||||
if (cval->initialized && cval->dBmin >= cval->dBmax) {
|
||||
ret = get_min_max_with_quirks(cval, 0, kcontrol);
|
||||
if ((ret >= 0 || ret == -EAGAIN) &&
|
||||
cval->initialized && cval->dBmin >= cval->dBmax) {
|
||||
kcontrol->vd[0].access &=
|
||||
~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
|
||||
@@ -1660,9 +1724,72 @@ static const struct usb_feature_control_info *get_feature_control_info(int contr
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool check_insane_volume_range(struct usb_mixer_interface *mixer,
|
||||
struct snd_kcontrol *kctl,
|
||||
struct usb_mixer_elem_info *cval)
|
||||
{
|
||||
int range, steps, threshold;
|
||||
|
||||
/*
|
||||
* If a device quirk has overrode our TLV callback, no warning should
|
||||
* be generated since our checks are only meaningful for dB volume.
|
||||
*/
|
||||
if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) ||
|
||||
kctl->tlv.c != snd_usb_mixer_vol_tlv)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Meaningless volume control capability (<1dB). This should cover
|
||||
* devices mapping their volume to val = 0/100/1, which are very likely
|
||||
* to be quirky.
|
||||
*/
|
||||
range = cval->max - cval->min;
|
||||
if (range < 256) {
|
||||
usb_audio_warn(mixer->chip,
|
||||
"Warning! Unlikely small volume range (=%u), linear volume or custom curve?",
|
||||
range);
|
||||
return true;
|
||||
}
|
||||
|
||||
steps = range / cval->res;
|
||||
|
||||
/*
|
||||
* There are definitely devices with ~20,000 ranges (e.g., HyperX Cloud
|
||||
* III with val = -18944/0/1), so we use some heuristics here:
|
||||
*
|
||||
* min < 0 < max: Attenuator + amplifier? Likely to be sane
|
||||
*
|
||||
* min < 0 = max: DSP? Voltage attenuator with FW conversion to dB?
|
||||
* Likely to be sane
|
||||
*
|
||||
* min < max < 0: Measured values? Neutral
|
||||
*
|
||||
* min = 0 < max: Oversimplified FW conversion? Linear volume? Likely to
|
||||
* be quirky (e.g., MV-SILICON)
|
||||
*
|
||||
* 0 < min < max: Amplifier with fixed gains? Likely to be quirky
|
||||
* (e.g., Logitech webcam)
|
||||
*/
|
||||
if (cval->min < 0 && 0 <= cval->max)
|
||||
threshold = 24576; /* 65535 * (3 / 8) */
|
||||
else if (cval->min < cval->max && cval->max < 0)
|
||||
threshold = 1024;
|
||||
else
|
||||
threshold = 384;
|
||||
|
||||
if (steps > threshold) {
|
||||
usb_audio_warn(mixer->chip,
|
||||
"Warning! Unlikely big volume step count (=%u), linear volume or wrong cval->res?",
|
||||
steps);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
const struct usbmix_name_map *imap,
|
||||
unsigned int ctl_mask, int control,
|
||||
u64 ctl_mask, int control,
|
||||
struct usb_audio_term *iterm,
|
||||
struct usb_audio_term *oterm,
|
||||
int unitid, int nameid, int readonly_mask)
|
||||
@@ -1673,7 +1800,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
struct snd_kcontrol *kctl;
|
||||
struct usb_mixer_elem_info *cval;
|
||||
const struct usbmix_name_map *map;
|
||||
unsigned int range;
|
||||
int ret;
|
||||
|
||||
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
||||
/* FIXME: not supported yet */
|
||||
@@ -1707,7 +1834,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
cval->master_readonly = readonly_mask;
|
||||
} else {
|
||||
int i, c = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
for (i = 0; i < MAX_CHANNELS; i++)
|
||||
if (ctl_mask & BIT(i))
|
||||
c++;
|
||||
cval->channels = c;
|
||||
@@ -1787,10 +1914,10 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
}
|
||||
|
||||
/* get min/max values */
|
||||
get_min_max_with_quirks(cval, 0, kctl);
|
||||
ret = get_min_max_with_quirks(cval, 0, kctl);
|
||||
|
||||
/* skip a bogus volume range */
|
||||
if (cval->max <= cval->min) {
|
||||
if ((ret < 0 && ret != -EAGAIN) || cval->max <= cval->min) {
|
||||
usb_audio_dbg(mixer->chip,
|
||||
"[%d] FU [%s] skipped due to invalid volume\n",
|
||||
cval->head.id, kctl->id.name);
|
||||
@@ -1811,29 +1938,21 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
|
||||
snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl);
|
||||
|
||||
range = (cval->max - cval->min) / cval->res;
|
||||
/*
|
||||
* There are definitely devices with a range of ~20,000, so let's be
|
||||
* conservative and allow for a bit more.
|
||||
*/
|
||||
if (range > 65535) {
|
||||
usb_audio_warn(mixer->chip,
|
||||
"Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
|
||||
range);
|
||||
usb_audio_warn(mixer->chip,
|
||||
"[%d] FU [%s] ch = %d, val = %d/%d/%d",
|
||||
if (check_insane_volume_range(mixer, kctl, cval)) {
|
||||
usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
|
||||
cval->head.id, kctl->id.name, cval->channels,
|
||||
cval->min, cval->max, cval->res);
|
||||
} else {
|
||||
usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
|
||||
cval->head.id, kctl->id.name, cval->channels,
|
||||
cval->min, cval->max, cval->res);
|
||||
}
|
||||
|
||||
usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
|
||||
cval->head.id, kctl->id.name, cval->channels,
|
||||
cval->min, cval->max, cval->res);
|
||||
snd_usb_mixer_add_control(&cval->head, kctl);
|
||||
}
|
||||
|
||||
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||
unsigned int ctl_mask, int control,
|
||||
u64 ctl_mask, int control,
|
||||
struct usb_audio_term *iterm, int unitid,
|
||||
int readonly_mask)
|
||||
{
|
||||
@@ -1845,7 +1964,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||
}
|
||||
|
||||
static void build_feature_ctl_badd(struct usb_mixer_interface *mixer,
|
||||
unsigned int ctl_mask, int control, int unitid,
|
||||
u64 ctl_mask, int control, int unitid,
|
||||
const struct usbmix_name_map *badd_map)
|
||||
{
|
||||
__build_feature_ctl(mixer, badd_map, ctl_mask, control,
|
||||
@@ -2021,7 +2140,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||
bmaControls = ftr->bmaControls;
|
||||
}
|
||||
|
||||
if (channels > 32) {
|
||||
if (channels > MAX_CHANNELS) {
|
||||
usb_audio_info(state->chip,
|
||||
"usbmixer: too many channels (%d) in unit %d\n",
|
||||
channels, unitid);
|
||||
@@ -2059,7 +2178,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||
if (state->mixer->protocol == UAC_VERSION_1) {
|
||||
/* check all control types */
|
||||
for (i = 0; i < 10; i++) {
|
||||
unsigned int ch_bits = 0;
|
||||
u64 ch_bits = 0;
|
||||
int control = audio_feature_info[i].control;
|
||||
|
||||
for (j = 0; j < channels; j++) {
|
||||
@@ -2085,7 +2204,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||
}
|
||||
} else { /* UAC_VERSION_2/3 */
|
||||
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
||||
unsigned int ch_bits = 0;
|
||||
u64 ch_bits = 0;
|
||||
unsigned int ch_read_only = 0;
|
||||
int control = audio_feature_info[i].control;
|
||||
|
||||
@@ -2172,6 +2291,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
|
||||
unsigned int i, len;
|
||||
struct snd_kcontrol *kctl;
|
||||
const struct usbmix_name_map *map;
|
||||
int ret;
|
||||
|
||||
map = find_map(state->map, unitid, 0);
|
||||
if (check_ignored_ctl(map))
|
||||
@@ -2194,7 +2314,11 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
|
||||
}
|
||||
|
||||
/* get min/max values */
|
||||
get_min_max(cval, 0);
|
||||
ret = get_min_max(cval, 0);
|
||||
if (ret < 0 && ret != -EAGAIN) {
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return;
|
||||
}
|
||||
|
||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
||||
if (!kctl) {
|
||||
@@ -2566,7 +2690,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
||||
break;
|
||||
}
|
||||
|
||||
get_min_max(cval, valinfo->min_value);
|
||||
err = get_min_max(cval, valinfo->min_value);
|
||||
break;
|
||||
}
|
||||
case USB_XU_CLOCK_RATE:
|
||||
@@ -2578,11 +2702,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
||||
cval->max = 5;
|
||||
cval->res = 1;
|
||||
cval->initialized = 1;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
get_min_max(cval, valinfo->min_value);
|
||||
err = get_min_max(cval, valinfo->min_value);
|
||||
break;
|
||||
}
|
||||
if (err < 0 && err != -EAGAIN) {
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = get_cur_ctl_value(cval, cval->control << 8, &val);
|
||||
if (err < 0) {
|
||||
@@ -3398,7 +3527,7 @@ static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
|
||||
[USB_MIXER_U32] = "U32",
|
||||
[USB_MIXER_BESPOKEN] = "BESPOKEN",
|
||||
};
|
||||
snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, "
|
||||
snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%llx, "
|
||||
"channels=%i, type=\"%s\"\n", cval->head.id,
|
||||
cval->control, cval->cmask, cval->channels,
|
||||
val_types[cval->val_type]);
|
||||
|
||||
@@ -44,7 +44,7 @@ struct usb_mixer_interface {
|
||||
void (*private_suspend)(struct usb_mixer_interface *mixer);
|
||||
};
|
||||
|
||||
#define MAX_CHANNELS 16 /* max logical channels */
|
||||
#define MAX_CHANNELS 64 /* max logical channels */
|
||||
|
||||
enum {
|
||||
USB_MIXER_BOOLEAN,
|
||||
@@ -81,7 +81,7 @@ struct usb_mixer_elem_list {
|
||||
struct usb_mixer_elem_info {
|
||||
struct usb_mixer_elem_list head;
|
||||
unsigned int control; /* CS or ICN (high byte) */
|
||||
unsigned int cmask; /* channel mask bitmap: 0 = master */
|
||||
u64 cmask; /* channel mask bitmap: 0 = master */
|
||||
unsigned int idx_off; /* Control index offset */
|
||||
unsigned int ch_readonly;
|
||||
unsigned int master_readonly;
|
||||
|
||||
@@ -4477,6 +4477,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
||||
case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */
|
||||
err = snd_sc1810_init_mixer(mixer);
|
||||
break;
|
||||
case USB_ID(0x194f, 0x0107): /* Presonus Studio 1824 */
|
||||
err = snd_sc1810_init_mixer(mixer);
|
||||
break;
|
||||
case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
|
||||
err = snd_bbfpro_controls_create(mixer);
|
||||
break;
|
||||
@@ -4588,6 +4591,24 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer,
|
||||
struct usb_mixer_elem_info *cval,
|
||||
struct snd_kcontrol *kctl)
|
||||
{
|
||||
if (cval->min == 0 && cval->max == 4096 && cval->res == 1) {
|
||||
/* The final effects will be printed later. */
|
||||
usb_audio_info(mixer->chip, "applying MV-SILICON quirks (0/4096/1 variant)\n");
|
||||
|
||||
/* Respect MIN_MUTE set by module parameters. */
|
||||
if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE))
|
||||
mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL;
|
||||
if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE))
|
||||
mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL;
|
||||
} else {
|
||||
usb_audio_dbg(mixer->chip, "not applying MV-SILICON quirks on unknown variant");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some Plantronics headsets have control names that don't meet ALSA naming
|
||||
* standards. This function fixes nonstandard source names. By the time
|
||||
@@ -4634,6 +4655,25 @@ static void snd_fix_plt_name(struct snd_usb_audio *chip,
|
||||
usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name);
|
||||
}
|
||||
|
||||
static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
|
||||
struct usb_mixer_elem_info *cval,
|
||||
struct snd_kcontrol *kctl)
|
||||
{
|
||||
static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0);
|
||||
|
||||
if (cval->min_mute) {
|
||||
/*
|
||||
* We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
|
||||
* resulting in min_mute being a no-op.
|
||||
*/
|
||||
usb_audio_warn(mixer->chip, "LINEAR_VOL overrides MIN_MUTE\n");
|
||||
}
|
||||
|
||||
kctl->tlv.p = scale;
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
|
||||
}
|
||||
|
||||
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
|
||||
struct usb_mixer_elem_info *cval, int unitid,
|
||||
struct snd_kcontrol *kctl)
|
||||
@@ -4645,6 +4685,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
|
||||
break;
|
||||
}
|
||||
|
||||
if (cval->control == UAC_FU_VOLUME &&
|
||||
!strncmp(mixer->chip->card->longname, "MV-SILICON", 10))
|
||||
snd_usb_mv_silicon_quirks(mixer, cval, kctl);
|
||||
|
||||
/* lowest playback value is muted on some devices */
|
||||
if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE)
|
||||
if (strstr(kctl->id.name, "Playback")) {
|
||||
@@ -4660,6 +4704,21 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
|
||||
"applying capture min mute quirk\n");
|
||||
cval->min_mute = 1;
|
||||
}
|
||||
|
||||
if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL)
|
||||
if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback")) {
|
||||
usb_audio_info(mixer->chip,
|
||||
"applying playback linear volume quirk\n");
|
||||
snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
|
||||
}
|
||||
|
||||
if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL)
|
||||
if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture")) {
|
||||
usb_audio_info(mixer->chip,
|
||||
"applying capture linear volume quirk\n");
|
||||
snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
|
||||
}
|
||||
|
||||
/* ALSA-ify some Plantronics headset control names */
|
||||
if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f &&
|
||||
(cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME))
|
||||
|
||||
@@ -362,6 +362,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
|
||||
snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB);
|
||||
break;
|
||||
|
||||
case USB_ID(0x194f, 0x0107): /* 1824 */
|
||||
case USB_ID(0x194f, 0x010d): /* 1824c */
|
||||
/* Set all output faders to unity gain */
|
||||
a = SC1810C_SEL_OUTPUT;
|
||||
@@ -685,6 +686,7 @@ int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case USB_ID(0x194f, 0x0107): /* Presonus Studio 1824 */
|
||||
case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */
|
||||
ret = snd_s1810c_switch_init(mixer, &snd_s1824c_mono_sw);
|
||||
if (ret < 0)
|
||||
|
||||
@@ -2262,7 +2262,7 @@ static const struct scarlett2_device_entry scarlett2_devices[] = {
|
||||
{ USB_ID(0x1235, 0x820c), &clarett_8pre_info, "Clarett+" },
|
||||
|
||||
/* End of list */
|
||||
{ 0, NULL },
|
||||
{ 0, NULL, NULL },
|
||||
};
|
||||
|
||||
/* get the starting port index number for a given port type/direction */
|
||||
|
||||
@@ -948,7 +948,7 @@ static int enable_audio_stream(struct snd_usb_substream *subs,
|
||||
_snd_pcm_hw_params_any(¶ms);
|
||||
|
||||
m = hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_leave(m, pcm_format);
|
||||
snd_mask_leave(m, (__force unsigned int)pcm_format);
|
||||
|
||||
i = hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
snd_interval_setinteger(i);
|
||||
|
||||
@@ -2652,6 +2652,54 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The AudioBox USB advertises S24_3LE as the only supported format
|
||||
* for both playback and capture. It does not support S16_LE despite
|
||||
* being a USB full-speed device.
|
||||
*/
|
||||
USB_DEVICE(0x194f, 0x0301),
|
||||
QUIRK_DRIVER_INFO {
|
||||
.vendor_name = "PreSonus",
|
||||
.product_name = "AudioBox USB",
|
||||
QUIRK_DATA_COMPOSITE {
|
||||
{ QUIRK_DATA_IGNORE(0) },
|
||||
{
|
||||
QUIRK_DATA_AUDIOFORMAT(2) {
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
||||
.channels = 2,
|
||||
.iface = 2,
|
||||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.attributes = 0,
|
||||
.endpoint = 0x01,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC,
|
||||
.rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 48000,
|
||||
}
|
||||
},
|
||||
{
|
||||
QUIRK_DATA_AUDIOFORMAT(3) {
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
||||
.channels = 2,
|
||||
.iface = 3,
|
||||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.attributes = 0,
|
||||
.endpoint = 0x82,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC,
|
||||
.rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 48000,
|
||||
}
|
||||
},
|
||||
QUIRK_COMPOSITE_END
|
||||
}
|
||||
}
|
||||
},
|
||||
#endif /* disabled */
|
||||
|
||||
{
|
||||
@@ -3900,5 +3948,70 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||
QUIRK_RME_DIGIFACE(0x3f8c),
|
||||
QUIRK_RME_DIGIFACE(0x3fa0),
|
||||
|
||||
#define QUIRK_AF16RIG(channel_count_, alt_setting_, \
|
||||
low_rate_, high_rate_, pack_size_, \
|
||||
clock_, interface_, endpoint_) \
|
||||
{ \
|
||||
QUIRK_DATA_AUDIOFORMAT(interface_) { \
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE, \
|
||||
.channels = channel_count_, \
|
||||
.fmt_type = UAC_FORMAT_TYPE_I_PCM, \
|
||||
.fmt_bits = 24, \
|
||||
.fmt_sz = 4, \
|
||||
.iface = interface_, \
|
||||
.altsetting = alt_setting_, \
|
||||
.altset_idx = alt_setting_, \
|
||||
.endpoint = endpoint_, \
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC | \
|
||||
USB_ENDPOINT_SYNC_ASYNC, \
|
||||
.datainterval = 1, \
|
||||
.protocol = UAC_VERSION_2, \
|
||||
.maxpacksize = pack_size_, \
|
||||
.rates = SNDRV_PCM_RATE_##low_rate_ | \
|
||||
SNDRV_PCM_RATE_##high_rate_, \
|
||||
.rate_min = low_rate_, \
|
||||
.rate_max = high_rate_, \
|
||||
.nr_rates = 2, \
|
||||
.rate_table = (unsigned int[]) { \
|
||||
low_rate_, high_rate_ }, \
|
||||
.clock = clock_, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define QUIRK_AF16RIG_CLOCK(clock) \
|
||||
QUIRK_AF16RIG(34, 1, 44100, 48000, 0x3b8, clock, 1, 0x01), \
|
||||
QUIRK_AF16RIG(34, 1, 44100, 48000, 0x3b8, clock, 2, 0x81), \
|
||||
QUIRK_AF16RIG(18, 2, 88200, 96000, 0x3a8, clock, 1, 0x01), \
|
||||
QUIRK_AF16RIG(18, 2, 88200, 96000, 0x3a8, clock, 2, 0x81), \
|
||||
QUIRK_AF16RIG(10, 3, 176400, 192000, 0x3e8, clock, 1, 0x01), \
|
||||
QUIRK_AF16RIG(10, 3, 176400, 192000, 0x3e8, clock, 2, 0x81)
|
||||
|
||||
/* Arturia AudioFuse 16Rig Audio */
|
||||
/* AF16Rig MIDI has USB PID 0xaf21 and appears to work OK without quirks */
|
||||
{
|
||||
USB_DEVICE(0x1c75, 0xaf20),
|
||||
QUIRK_DRIVER_INFO {
|
||||
.vendor_name = "Arturia",
|
||||
.product_name = "AF16Rig",
|
||||
QUIRK_DATA_COMPOSITE {
|
||||
{ QUIRK_DATA_STANDARD_MIXER(0) },
|
||||
QUIRK_AF16RIG_CLOCK(41), /* Internal clock */
|
||||
#if 0
|
||||
/* These are disabled because I don't have the required hardware to test
|
||||
* them. I suspect that the ADAT clock might not follow 176400 or 192000
|
||||
* because the AF16Rig won't accept ADAT audio data at those rates.
|
||||
*/
|
||||
QUIRK_AF16RIG_CLOCK(43), /* ADAT clock */
|
||||
QUIRK_AF16RIG_CLOCK(44), /* BNC word clock */
|
||||
#endif
|
||||
{ QUIRK_DATA_IGNORE(3) }, /* Firmware update */
|
||||
QUIRK_COMPOSITE_END
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
#undef QUIRK_AF16RIG_CLOCK
|
||||
#undef QUIRK_AF16RIG
|
||||
|
||||
#undef USB_DEVICE_VENDOR_SPEC
|
||||
#undef USB_AUDIO_DEVICE
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi.h>
|
||||
@@ -2135,16 +2138,69 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
||||
/*
|
||||
* driver behavior quirk flags
|
||||
*/
|
||||
struct usb_string_match {
|
||||
const char *manufacturer;
|
||||
const char *product;
|
||||
};
|
||||
|
||||
struct usb_audio_quirk_flags_table {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
const struct usb_string_match *usb_string_match;
|
||||
};
|
||||
|
||||
#define DEVICE_FLG(vid, pid, _flags) \
|
||||
{ .id = USB_ID(vid, pid), .flags = (_flags) }
|
||||
#define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
|
||||
|
||||
/*
|
||||
* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts.
|
||||
*
|
||||
* Usage:
|
||||
* // match vid, pid, "manufacturer", and "product"
|
||||
* DEVICE_STRING_FLG(vid, pid, "manufacturer", "product", flags)
|
||||
*
|
||||
* // match vid, pid, "manufacturer", and any product string
|
||||
* DEVICE_STRING_FLG(vid, pid, "manufacturer", NULL, flags)
|
||||
*
|
||||
* // match vid, pid, "manufacturer", and device must have no product string
|
||||
* DEVICE_STRING_FLG(vid, pid, "manufacturer", "", flags)
|
||||
*
|
||||
* // match vid, pid, any manufacturer string, and "product"
|
||||
* DEVICE_STRING_FLG(vid, pid, NULL, "product", flags)
|
||||
*
|
||||
* // match vid, pid, no manufacturer string, and "product"
|
||||
* DEVICE_STRING_FLG(vid, pid, "", "product", flags)
|
||||
*
|
||||
* // match vid, pid, no manufacturer string, and no product string
|
||||
* DEVICE_STRING_FLG(vid, pid, "", "", flags)
|
||||
*/
|
||||
#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags) \
|
||||
{ \
|
||||
.id = USB_ID(vid, pid), \
|
||||
.usb_string_match = &(const struct usb_string_match) { \
|
||||
.manufacturer = _manufacturer, \
|
||||
.product = _product, \
|
||||
}, \
|
||||
.flags = (_flags), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts.
|
||||
*
|
||||
* Usage:
|
||||
* // match vid, and "manufacturer"
|
||||
* VENDOR_STRING_FLG(vid, "manufacturer", flags)
|
||||
*
|
||||
* // match vid, and device must have no manufacturer string
|
||||
* VENDOR_STRING_FLG(vid, "", flags)
|
||||
*/
|
||||
#define VENDOR_STRING_FLG(vid, _manufacturer, _flags) \
|
||||
DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags)
|
||||
|
||||
static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
/* Device and string descriptor matches */
|
||||
|
||||
/* Device matches */
|
||||
DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
|
||||
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
|
||||
@@ -2281,6 +2337,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
|
||||
DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */
|
||||
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
|
||||
DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
|
||||
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
|
||||
@@ -2291,8 +2349,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
|
||||
DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */
|
||||
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
|
||||
DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */
|
||||
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE |
|
||||
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
|
||||
DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
|
||||
DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */
|
||||
@@ -2421,6 +2480,13 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
|
||||
QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x84ef, 0x0082, /* Hotone Audio Pulze Mini */
|
||||
QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL),
|
||||
|
||||
/* Vendor and string descriptor matches */
|
||||
VENDOR_STRING_FLG(0x1235, /* Conflict with Focusrite Novation */
|
||||
"MV-SILICON",
|
||||
0), /* Stop matching */
|
||||
|
||||
/* Vendor matches */
|
||||
VENDOR_FLG(0x045e, /* MS Lifecam */
|
||||
@@ -2522,6 +2588,8 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
|
||||
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_MIN_MUTE),
|
||||
QUIRK_STRING_ENTRY(MIXER_CAPTURE_MIN_MUTE),
|
||||
QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP),
|
||||
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
|
||||
QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -2578,6 +2646,16 @@ void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
|
||||
if (chip->usb_id == p->id ||
|
||||
(!USB_ID_PRODUCT(p->id) &&
|
||||
USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
|
||||
/* Handle DEVICE_STRING_FLG/VENDOR_STRING_FLG. */
|
||||
if (p->usb_string_match && p->usb_string_match->manufacturer &&
|
||||
strcmp(p->usb_string_match->manufacturer,
|
||||
chip->dev->manufacturer ? chip->dev->manufacturer : ""))
|
||||
continue;
|
||||
if (p->usb_string_match && p->usb_string_match->product &&
|
||||
strcmp(p->usb_string_match->product,
|
||||
chip->dev->product ? chip->dev->product : ""))
|
||||
continue;
|
||||
|
||||
snd_usb_apply_flag_dbg("builtin table", chip, p->flags);
|
||||
chip->quirk_flags |= p->flags;
|
||||
return;
|
||||
|
||||
@@ -366,6 +366,8 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
|
||||
/*
|
||||
* TODO: this conversion is not complete, update it
|
||||
* after adding UAC3 values to asound.h
|
||||
* NOTE: not all UAC3 channel relationship have a
|
||||
* direct ALSA chmap equivalent.
|
||||
*/
|
||||
switch (is->bChRelationship) {
|
||||
case UAC3_CH_MONO:
|
||||
@@ -390,6 +392,12 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
|
||||
case UAC3_CH_FRONT_RIGHT_OF_CENTER:
|
||||
map = SNDRV_CHMAP_FRC;
|
||||
break;
|
||||
case UAC3_CH_FRONT_WIDE_LEFT:
|
||||
map = SNDRV_CHMAP_FLW;
|
||||
break;
|
||||
case UAC3_CH_FRONT_WIDE_RIGHT:
|
||||
map = SNDRV_CHMAP_FRW;
|
||||
break;
|
||||
case UAC3_CH_SIDE_LEFT:
|
||||
map = SNDRV_CHMAP_SL;
|
||||
break;
|
||||
|
||||
@@ -228,6 +228,14 @@ extern bool snd_usb_skip_validation;
|
||||
* Skip the probe-time interface setup (usb_set_interface,
|
||||
* init_pitch, init_sample_rate); redundant with
|
||||
* snd_usb_endpoint_prepare() at stream-open time
|
||||
* QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL
|
||||
* Set linear volume mapping for devices where the playback volume control
|
||||
* value is mapped to voltage (instead of dB) level linearly. In short:
|
||||
* x(raw) = (raw - raw_min) / (raw_max - raw_min); V(x) = k * x;
|
||||
* dB(x) = 20 * log10(x). Overrides QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE
|
||||
* QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL
|
||||
* Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams.
|
||||
* Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE
|
||||
*/
|
||||
|
||||
enum {
|
||||
@@ -258,6 +266,8 @@ enum {
|
||||
QUIRK_TYPE_MIXER_PLAYBACK_MIN_MUTE = 24,
|
||||
QUIRK_TYPE_MIXER_CAPTURE_MIN_MUTE = 25,
|
||||
QUIRK_TYPE_SKIP_IFACE_SETUP = 26,
|
||||
QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27,
|
||||
QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
|
||||
/* Please also edit snd_usb_audio_quirk_flag_names */
|
||||
};
|
||||
|
||||
@@ -290,5 +300,7 @@ enum {
|
||||
#define QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE QUIRK_FLAG(MIXER_PLAYBACK_MIN_MUTE)
|
||||
#define QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE QUIRK_FLAG(MIXER_CAPTURE_MIN_MUTE)
|
||||
#define QUIRK_FLAG_SKIP_IFACE_SETUP QUIRK_FLAG(SKIP_IFACE_SETUP)
|
||||
#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
|
||||
#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
|
||||
|
||||
#endif /* __USBAUDIO_H */
|
||||
|
||||
@@ -420,7 +420,11 @@ static int tascam_probe(struct usb_interface *intf,
|
||||
|
||||
/* The device has two interfaces; we drive both from this driver. */
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
|
||||
tascam = usb_get_intfdata(usb_ifnum_to_if(dev, 0));
|
||||
struct usb_interface *intf_zero = usb_ifnum_to_if(dev, 0);
|
||||
|
||||
if (!intf_zero)
|
||||
return -ENODEV;
|
||||
tascam = usb_get_intfdata(intf_zero);
|
||||
if (tascam) {
|
||||
usb_set_intfdata(intf, tascam);
|
||||
tascam->iface1 = intf;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define FRAME_RATE 8000
|
||||
#define PERIOD_SIZE 4410
|
||||
@@ -52,7 +53,14 @@ FIXTURE_SETUP(timer_f) {
|
||||
timer_dev_fd = open("/dev/snd/timer", O_RDONLY);
|
||||
ASSERT_GE(timer_dev_fd, 0);
|
||||
|
||||
ASSERT_EQ(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info), 0);
|
||||
if (ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info) < 0) {
|
||||
int err = errno;
|
||||
|
||||
close(timer_dev_fd);
|
||||
if (err == ENOTTY || err == ENXIO)
|
||||
SKIP(return, "CONFIG_SND_UTIMER not enabled");
|
||||
ASSERT_EQ(err, 0);
|
||||
}
|
||||
ASSERT_GE(self->utimer_info->fd, 0);
|
||||
|
||||
close(timer_dev_fd);
|
||||
|
||||
Reference in New Issue
Block a user