Commit 44c76a96 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/misc' into for-linus

parents dbf117cb c6b76d1f
...@@ -264,7 +264,7 @@ struct snd_pcm_hw_constraint_ratdens { ...@@ -264,7 +264,7 @@ struct snd_pcm_hw_constraint_ratdens {
struct snd_pcm_hw_constraint_list { struct snd_pcm_hw_constraint_list {
unsigned int count; unsigned int count;
unsigned int *list; const unsigned int *list;
unsigned int mask; unsigned int mask;
}; };
...@@ -781,7 +781,8 @@ void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interva ...@@ -781,7 +781,8 @@ void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interva
unsigned int k, struct snd_interval *c); unsigned int k, struct snd_interval *c);
void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
const struct snd_interval *b, struct snd_interval *c); const struct snd_interval *b, struct snd_interval *c);
int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask); int snd_interval_list(struct snd_interval *i, unsigned int count,
const unsigned int *list, unsigned int mask);
int snd_interval_ratnum(struct snd_interval *i, int snd_interval_ratnum(struct snd_interval *i,
unsigned int rats_count, struct snd_ratnum *rats, unsigned int rats_count, struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp); unsigned int *nump, unsigned int *denp);
......
/* include/version.h */ /* include/version.h */
#define CONFIG_SND_VERSION "1.0.24" #define CONFIG_SND_VERSION "1.0.25"
#define CONFIG_SND_DATE "" #define CONFIG_SND_DATE ""
...@@ -366,6 +366,8 @@ struct snd_ymfpci { ...@@ -366,6 +366,8 @@ struct snd_ymfpci {
#ifdef CONFIG_PM #ifdef CONFIG_PM
u32 *saved_regs; u32 *saved_regs;
u32 saved_ydsxgr_mode; u32 saved_ydsxgr_mode;
u16 saved_dsxg_legacy;
u16 saved_dsxg_elegacy;
#endif #endif
}; };
......
...@@ -1132,15 +1132,4 @@ static struct i2c_driver onyx_driver = { ...@@ -1132,15 +1132,4 @@ static struct i2c_driver onyx_driver = {
.id_table = onyx_i2c_id, .id_table = onyx_i2c_id,
}; };
static int __init onyx_init(void) module_i2c_driver(onyx_driver);
{
return i2c_add_driver(&onyx_driver);
}
static void __exit onyx_exit(void)
{
i2c_del_driver(&onyx_driver);
}
module_init(onyx_init);
module_exit(onyx_exit);
...@@ -1026,15 +1026,4 @@ static struct i2c_driver tas_driver = { ...@@ -1026,15 +1026,4 @@ static struct i2c_driver tas_driver = {
.id_table = tas_i2c_id, .id_table = tas_i2c_id,
}; };
static int __init tas_init(void) module_i2c_driver(tas_driver);
{
return i2c_add_driver(&tas_driver);
}
static void __exit tas_exit(void)
{
i2c_del_driver(&tas_driver);
}
module_init(tas_init);
module_exit(tas_exit);
...@@ -480,74 +480,104 @@ int snd_card_free(struct snd_card *card) ...@@ -480,74 +480,104 @@ int snd_card_free(struct snd_card *card)
EXPORT_SYMBOL(snd_card_free); EXPORT_SYMBOL(snd_card_free);
static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) /* retrieve the last word of shortname or longname */
static const char *retrieve_id_from_card_name(const char *name)
{ {
int i, len, idx_flag = 0, loops = SNDRV_CARDS; const char *spos = name;
const char *spos, *src;
char *id;
if (nid == NULL) { while (*name) {
id = card->shortname; if (isspace(*name) && isalnum(name[1]))
spos = src = id; spos = name + 1;
while (*id != '\0') { name++;
if (*id == ' ')
spos = id + 1;
id++;
} }
} else { return spos;
spos = src = nid; }
/* return true if the given id string doesn't conflict any other card ids */
static bool card_id_ok(struct snd_card *card, const char *id)
{
int i;
if (!snd_info_check_reserved_words(id))
return false;
for (i = 0; i < snd_ecards_limit; i++) {
if (snd_cards[i] && snd_cards[i] != card &&
!strcmp(snd_cards[i]->id, id))
return false;
} }
id = card->id; return true;
while (*spos != '\0' && !isalnum(*spos)) }
spos++;
if (isdigit(*spos)) /* copy to card->id only with valid letters from nid */
*id++ = isalpha(src[0]) ? src[0] : 'D'; static void copy_valid_id_string(struct snd_card *card, const char *src,
while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { const char *nid)
if (isalnum(*spos)) {
*id++ = *spos; char *id = card->id;
spos++;
while (*nid && !isalnum(*nid))
nid++;
if (isdigit(*nid))
*id++ = isalpha(*src) ? *src : 'D';
while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
if (isalnum(*nid))
*id++ = *nid;
nid++;
} }
*id = '\0'; *id = 0;
}
/* Set card->id from the given string
* If the string conflicts with other ids, add a suffix to make it unique.
*/
static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
const char *nid)
{
int len, loops;
bool with_suffix;
bool is_default = false;
char *id;
copy_valid_id_string(card, src, nid);
id = card->id; id = card->id;
if (*id == '\0') again:
/* use "Default" for obviously invalid strings
* ("card" conflicts with proc directories)
*/
if (!*id || !strncmp(id, "card", 4)) {
strcpy(id, "Default"); strcpy(id, "Default");
is_default = true;
while (1) {
if (loops-- == 0) {
snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
strcpy(card->id, card->proc_root->name);
return;
}
if (!snd_info_check_reserved_words(id))
goto __change;
for (i = 0; i < snd_ecards_limit; i++) {
if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
goto __change;
} }
break;
__change: with_suffix = false;
for (loops = 0; loops < SNDRV_CARDS; loops++) {
if (card_id_ok(card, id))
return; /* OK */
len = strlen(id); len = strlen(id);
if (idx_flag) { if (!with_suffix) {
if (id[len-1] != '9') /* add the "_X" suffix */
id[len-1]++; char *spos = id + len;
else if (len > sizeof(card->id) - 3)
id[len-1] = 'A'; spos = id + sizeof(card->id) - 3;
} else if ((size_t)len <= sizeof(card->id) - 3) { strcpy(spos, "_1");
strcat(id, "_1"); with_suffix = true;
idx_flag++;
} else { } else {
spos = id + len - 2; /* modify the existing suffix */
if ((size_t)len <= sizeof(card->id) - 2) if (id[len - 1] != '9')
spos++; id[len - 1]++;
*(char *)spos++ = '_'; else
*(char *)spos++ = '1'; id[len - 1] = 'A';
*(char *)spos++ = '\0'; }
idx_flag++;
} }
/* fallback to the default id */
if (!is_default) {
*id = 0;
goto again;
} }
/* last resort... */
snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
if (card->proc_root->name)
strcpy(card->id, card->proc_root->name);
} }
/** /**
...@@ -564,7 +594,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid) ...@@ -564,7 +594,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
if (card->id[0] != '\0') if (card->id[0] != '\0')
return; return;
mutex_lock(&snd_card_mutex); mutex_lock(&snd_card_mutex);
snd_card_set_id_no_lock(card, nid); snd_card_set_id_no_lock(card, nid, nid);
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
} }
EXPORT_SYMBOL(snd_card_set_id); EXPORT_SYMBOL(snd_card_set_id);
...@@ -596,22 +626,12 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, ...@@ -596,22 +626,12 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
memcpy(buf1, buf, copy); memcpy(buf1, buf, copy);
buf1[copy] = '\0'; buf1[copy] = '\0';
mutex_lock(&snd_card_mutex); mutex_lock(&snd_card_mutex);
if (!snd_info_check_reserved_words(buf1)) { if (!card_id_ok(NULL, buf1)) {
__exist:
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
return -EEXIST; return -EEXIST;
} }
for (idx = 0; idx < snd_ecards_limit; idx++) {
if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
if (card == snd_cards[idx])
goto __ok;
else
goto __exist;
}
}
strcpy(card->id, buf1); strcpy(card->id, buf1);
snd_info_card_id_change(card); snd_info_card_id_change(card);
__ok:
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
return count; return count;
...@@ -665,7 +685,18 @@ int snd_card_register(struct snd_card *card) ...@@ -665,7 +685,18 @@ int snd_card_register(struct snd_card *card)
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
return 0; return 0;
} }
snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); if (*card->id) {
/* make a unique id name from the given string */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
} else {
/* create an id from either shortname or longname */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src,
retrieve_id_from_card_name(src));
}
snd_cards[card->number] = card; snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
init_info_for_card(card); init_info_for_card(card);
......
...@@ -1029,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i, ...@@ -1029,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i,
* *
* Returns non-zero if the value is changed, zero if not changed. * Returns non-zero if the value is changed, zero if not changed.
*/ */
int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) int snd_interval_list(struct snd_interval *i, unsigned int count,
const unsigned int *list, unsigned int mask)
{ {
unsigned int k; unsigned int k;
struct snd_interval list_range; struct snd_interval list_range;
......
...@@ -1586,12 +1586,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -1586,12 +1586,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
struct file *file; struct file *file;
struct snd_pcm_file *pcm_file; struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1; struct snd_pcm_substream *substream1;
struct snd_pcm_group *group;
file = snd_pcm_file_fd(fd); file = snd_pcm_file_fd(fd);
if (!file) if (!file)
return -EBADFD; return -EBADFD;
pcm_file = file->private_data; pcm_file = file->private_data;
substream1 = pcm_file->substream; substream1 = pcm_file->substream;
group = kmalloc(sizeof(*group), GFP_KERNEL);
if (!group) {
res = -ENOMEM;
goto _nolock;
}
down_write(&snd_pcm_link_rwsem); down_write(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock); write_lock_irq(&snd_pcm_link_rwlock);
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
...@@ -1604,11 +1610,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -1604,11 +1610,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
goto _end; goto _end;
} }
if (!snd_pcm_stream_linked(substream)) { if (!snd_pcm_stream_linked(substream)) {
substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); substream->group = group;
if (substream->group == NULL) {
res = -ENOMEM;
goto _end;
}
spin_lock_init(&substream->group->lock); spin_lock_init(&substream->group->lock);
INIT_LIST_HEAD(&substream->group->substreams); INIT_LIST_HEAD(&substream->group->substreams);
list_add_tail(&substream->link_list, &substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams);
...@@ -1620,7 +1622,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -1620,7 +1622,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
_end: _end:
write_unlock_irq(&snd_pcm_link_rwlock); write_unlock_irq(&snd_pcm_link_rwlock);
up_write(&snd_pcm_link_rwsem); up_write(&snd_pcm_link_rwsem);
_nolock:
fput(file); fput(file);
if (res < 0)
kfree(group);
return res; return res;
} }
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/hwdep.h> #include <sound/hwdep.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/tlv.h>
#endif #endif
#ifndef CHIP_AU8820 #ifndef CHIP_AU8820
...@@ -107,6 +107,14 @@ ...@@ -107,6 +107,14 @@
#define NR_WTPB 0x20 /* WT channels per each bank. */ #define NR_WTPB 0x20 /* WT channels per each bank. */
#define NR_PCM 0x10 #define NR_PCM 0x10
struct pcm_vol {
struct snd_kcontrol *kctl;
int active;
int dma;
int mixin[4];
int vol[4];
};
/* Structs */ /* Structs */
typedef struct { typedef struct {
//int this_08; /* Still unknown */ //int this_08; /* Still unknown */
...@@ -168,6 +176,7 @@ struct snd_vortex { ...@@ -168,6 +176,7 @@ struct snd_vortex {
/* Xtalk canceler */ /* Xtalk canceler */
int xt_mode; /* 1: speakers, 0:headphones. */ int xt_mode; /* 1: speakers, 0:headphones. */
#endif #endif
struct pcm_vol pcm_vol[NR_PCM];
int isquad; /* cache of extended ID codec flag. */ int isquad; /* cache of extended ID codec flag. */
...@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt); ...@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt);
/* Connection stuff. */ /* Connection stuff. */
static void vortex_connect_default(vortex_t * vortex, int en); static void vortex_connect_default(vortex_t * vortex, int en);
static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
int dir, int type); int dir, int type, int subdev);
static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
int restype); int restype);
#ifndef CHIP_AU8810 #ifndef CHIP_AU8810
......
...@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) ...@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
} }
/* Default Connections */ /* Default Connections */
static int
vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
static void vortex_connect_default(vortex_t * vortex, int en) static void vortex_connect_default(vortex_t * vortex, int en)
{ {
...@@ -2111,14 +2109,12 @@ static void vortex_connect_default(vortex_t * vortex, int en) ...@@ -2111,14 +2109,12 @@ static void vortex_connect_default(vortex_t * vortex, int en)
Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0. Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
*/ */
static int static int
vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
int type, int subdev)
{ {
stream_t *stream; stream_t *stream;
int i, en; int i, en;
struct pcm_vol *p;
if ((nr_ch == 3)
|| ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
return -EBUSY;
if (dma >= 0) { if (dma >= 0) {
en = 0; en = 0;
...@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) ...@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
MIX_DEFIGAIN); MIX_DEFIGAIN);
#endif #endif
} }
if (stream->type == VORTEX_PCM_ADB && en) {
p = &vortex->pcm_vol[subdev];
p->dma = dma;
for (i = 0; i < nr_ch; i++)
p->mixin[i] = mix[i];
for (i = 0; i < ch_top; i++)
p->vol[i] = 0;
}
} }
#ifndef CHIP_AU8820 #ifndef CHIP_AU8820
else { else {
...@@ -2473,7 +2477,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) ...@@ -2473,7 +2477,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_STAT); hwread(vortex->mmio, VORTEX_IRQ_STAT);
handled = 1; handled = 1;
} }
if (source & IRQ_MIDI) { if ((source & IRQ_MIDI) && vortex->rmidi) {
snd_mpu401_uart_interrupt(vortex->irq, snd_mpu401_uart_interrupt(vortex->irq,
vortex->rmidi->private_data); vortex->rmidi->private_data);
handled = 1; handled = 1;
......
...@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { ...@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
.mask = 0, .mask = 0,
}; };
#endif #endif
static void vortex_notify_pcm_vol_change(struct snd_card *card,
struct snd_kcontrol *kctl, int activate)
{
if (activate)
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
else
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
}
/* open callback */ /* open callback */
static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
{ {
...@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
if (stream != NULL) if (stream != NULL)
vortex_adb_allocroute(chip, stream->dma, vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir, stream->nr_ch, stream->dir,
stream->type); stream->type,
substream->number);
/* Alloc routes. */ /* Alloc routes. */
dma = dma =
vortex_adb_allocroute(chip, -1, vortex_adb_allocroute(chip, -1,
params_channels(hw_params), params_channels(hw_params),
substream->stream, type); substream->stream, type,
substream->number);
if (dma < 0) { if (dma < 0) {
spin_unlock_irq(&chip->lock); spin_unlock_irq(&chip->lock);
return dma; return dma;
...@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
vortex_adbdma_setbuffers(chip, dma, vortex_adbdma_setbuffers(chip, dma,
params_period_bytes(hw_params), params_period_bytes(hw_params),
params_periods(hw_params)); params_periods(hw_params));
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
chip->pcm_vol[substream->number].active = 1;
vortex_notify_pcm_vol_change(chip->card,
chip->pcm_vol[substream->number].kctl, 1);
}
} }
#ifndef CHIP_AU8810 #ifndef CHIP_AU8810
else { else {
...@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
spin_lock_irq(&chip->lock); spin_lock_irq(&chip->lock);
// Delete audio routes. // Delete audio routes.
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
if (stream != NULL) if (stream != NULL) {
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
chip->pcm_vol[substream->number].active = 0;
vortex_notify_pcm_vol_change(chip->card,
chip->pcm_vol[substream->number].kctl,
0);
}
vortex_adb_allocroute(chip, stream->dma, vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir, stream->nr_ch, stream->dir,
stream->type); stream->type,
substream->number);
}
} }
#ifndef CHIP_AU8810 #ifndef CHIP_AU8810
else { else {
...@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { ...@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
}, },
}; };
/* subdevice PCM Volume control */
static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
vortex_t *vortex = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
uinfo->value.integer.min = -128;
uinfo->value.integer.max = 32;
return 0;
}
static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i;
vortex_t *vortex = snd_kcontrol_chip(kcontrol);
int subdev = kcontrol->id.subdevice;
struct pcm_vol *p = &vortex->pcm_vol[subdev];
int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
for (i = 0; i < max_chn; i++)
ucontrol->value.integer.value[i] = p->vol[i];
return 0;
}
static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i;
int changed = 0;
int mixin;
unsigned char vol;
vortex_t *vortex = snd_kcontrol_chip(kcontrol);
int subdev = kcontrol->id.subdevice;
struct pcm_vol *p = &vortex->pcm_vol[subdev];
int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
for (i = 0; i < max_chn; i++) {
if (p->vol[i] != ucontrol->value.integer.value[i]) {
p->vol[i] = ucontrol->value.integer.value[i];
if (p->active) {
switch (vortex->dma_adb[p->dma].nr_ch) {
case 1:
mixin = p->mixin[0];
break;
case 2:
default:
mixin = p->mixin[(i < 2) ? i : (i - 2)];
break;
case 4:
mixin = p->mixin[i];
break;
};
vol = p->vol[i];
vortex_mix_setinputvolumebyte(vortex,
vortex->mixplayb[i], mixin, vol);
}
changed = 1;
}
}
return changed;
}
static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.info = snd_vortex_pcm_vol_info,
.get = snd_vortex_pcm_vol_get,
.put = snd_vortex_pcm_vol_put,
.tlv = { .p = vortex_pcm_vol_db_scale },
};
/* create a pcm device */ /* create a pcm device */
static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
{ {
...@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) ...@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
return err; return err;
} }
} }
if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
for (i = 0; i < NR_PCM; i++) {
chip->pcm_vol[i].active = 0;
chip->pcm_vol[i].dma = -1;
kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
if (!kctl)
return -ENOMEM;
chip->pcm_vol[i].kctl = kctl;
kctl->id.device = 0;
kctl->id.subdevice = i;
err = snd_ctl_add(chip->card, kctl);
if (err < 0)
return err;
}
}
return 0; return 0;
} }
...@@ -36,7 +36,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size) ...@@ -36,7 +36,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
size = CT_PAGE_ALIGN(size); size = CT_PAGE_ALIGN(size);
if (size > vm->size) { if (size > vm->size) {
printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " printk(KERN_ERR "ctxfi: Fail! No sufficient device virtual "
"memory space available!\n"); "memory space available!\n");
return NULL; return NULL;
} }
......
...@@ -1013,6 +1013,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice, ...@@ -1013,6 +1013,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice,
ice->hw_rates); ice->hw_rates);
} }
/* if the card has the internal rate locked (is_pro_locked), limit runtime
hw rates to the current internal rate only.
*/
static void constrain_rate_if_locked(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int rate;
if (is_pro_rate_locked(ice)) {
rate = ice->get_rate(ice);
if (rate >= runtime->hw.rate_min
&& rate <= runtime->hw.rate_max) {
runtime->hw.rate_min = rate;
runtime->hw.rate_max = rate;
}
}
}
/* multi-channel playback needs alignment 8x32bit regardless of the channels /* multi-channel playback needs alignment 8x32bit regardless of the channels
* actually used * actually used
*/ */
...@@ -1046,6 +1065,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream) ...@@ -1046,6 +1065,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
constrain_rate_if_locked(substream);
if (ice->pro_open) if (ice->pro_open)
ice->pro_open(ice, substream); ice->pro_open(ice, substream);
return 0; return 0;
...@@ -1066,6 +1086,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream) ...@@ -1066,6 +1086,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
constrain_rate_if_locked(substream);
if (ice->pro_open) if (ice->pro_open)
ice->pro_open(ice, substream); ice->pro_open(ice, substream);
return 0; return 0;
...@@ -1215,6 +1236,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) ...@@ -1215,6 +1236,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
constrain_rate_if_locked(substream);
if (ice->spdif.ops.open) if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream); ice->spdif.ops.open(ice, substream);
return 0; return 0;
...@@ -1251,6 +1273,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) ...@@ -1251,6 +1273,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
constrain_rate_if_locked(substream);
if (ice->spdif.ops.open) if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream); ice->spdif.ops.open(ice, substream);
return 0; return 0;
......
...@@ -2317,6 +2317,10 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) ...@@ -2317,6 +2317,10 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY,
&chip->saved_dsxg_legacy);
pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY,
&chip->saved_dsxg_elegacy);
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
snd_ymfpci_disable_dsp(chip); snd_ymfpci_disable_dsp(chip);
...@@ -2351,6 +2355,11 @@ int snd_ymfpci_resume(struct pci_dev *pci) ...@@ -2351,6 +2355,11 @@ int snd_ymfpci_resume(struct pci_dev *pci)
snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97);
pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY,
chip->saved_dsxg_legacy);
pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY,
chip->saved_dsxg_elegacy);
/* start hw again */ /* start hw again */
if (chip->start_count > 0) { if (chip->start_count > 0) {
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
......
...@@ -1112,17 +1112,7 @@ static struct spi_driver at73c213_driver = { ...@@ -1112,17 +1112,7 @@ static struct spi_driver at73c213_driver = {
.remove = __devexit_p(snd_at73c213_remove), .remove = __devexit_p(snd_at73c213_remove),
}; };
static int __init at73c213_init(void) module_spi_driver(at73c213_driver);
{
return spi_register_driver(&at73c213_driver);
}
module_init(at73c213_init);
static void __exit at73c213_exit(void)
{
spi_unregister_driver(&at73c213_driver);
}
module_exit(at73c213_exit);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC"); MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -29,7 +28,7 @@ ...@@ -29,7 +28,7 @@
#include <sound/initval.h> #include <sound/initval.h>
MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>"); MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0"); MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}"); MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* Thanks to:
* - Holger Ruckdeschel: he found out how to control individual channel
* volumes and introduced mute switch
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
...@@ -16,6 +19,7 @@ ...@@ -16,6 +19,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h>
#include "control.h" #include "control.h"
#include "comm.h" #include "comm.h"
...@@ -24,26 +28,6 @@ ...@@ -24,26 +28,6 @@
static char *opt_coax_texts[2] = { "Optical", "Coax" }; static char *opt_coax_texts[2] = { "Optical", "Coax" };
static char *line_phono_texts[2] = { "Line", "Phono" }; static char *line_phono_texts[2] = { "Line", "Phono" };
/*
* calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$
* this is done because the linear values cause rapid degredation
* of volume in the uppermost region.
*/
static const u8 log_volume_table[128] = {
0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34,
0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43,
0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56,
0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c,
0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75,
0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79,
0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f };
/* /*
* data that needs to be sent to device. sets up card internal stuff. * data that needs to be sent to device. sets up card internal stuff.
* values dumped from windows driver and filtered by trial'n'error. * values dumped from windows driver and filtered by trial'n'error.
...@@ -59,7 +43,7 @@ init_data[] = { ...@@ -59,7 +43,7 @@ init_data[] = {
{ 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
{ 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
{ 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
{ 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
{ 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
{ 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
{ 0 } /* TERMINATING ENTRY */ { 0 } /* TERMINATING ENTRY */
...@@ -70,19 +54,46 @@ static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; ...@@ -70,19 +54,46 @@ static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);
enum { enum {
DIGITAL_THRU_ONLY_SAMPLERATE = 3 DIGITAL_THRU_ONLY_SAMPLERATE = 3
}; };
static void usb6fire_control_master_vol_update(struct control_runtime *rt) static void usb6fire_control_output_vol_update(struct control_runtime *rt)
{ {
struct comm_runtime *comm_rt = rt->chip->comm; struct comm_runtime *comm_rt = rt->chip->comm;
if (comm_rt) { int i;
/* set volume */
comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f - if (comm_rt)
log_volume_table[rt->master_vol]); for (i = 0; i < 6; i++)
/* unmute */ if (!(rt->ovol_updated & (1 << i))) {
comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00); comm_rt->write8(comm_rt, 0x12, 0x0f + i,
180 - rt->output_vol[i]);
rt->ovol_updated |= 1 << i;
}
}
static void usb6fire_control_output_mute_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
if (comm_rt)
comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);
}
static void usb6fire_control_input_vol_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
int i;
if (comm_rt)
for (i = 0; i < 2; i++)
if (!(rt->ivol_updated & (1 << i))) {
comm_rt->write8(comm_rt, 0x12, 0x1c + i,
rt->input_vol[i] & 0x3f);
rt->ivol_updated |= 1 << i;
} }
} }
...@@ -165,34 +176,147 @@ static int usb6fire_control_streaming_update(struct control_runtime *rt) ...@@ -165,34 +176,147 @@ static int usb6fire_control_streaming_update(struct control_runtime *rt)
return -EINVAL; return -EINVAL;
} }
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1; uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 180;
return 0;
}
static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
unsigned int ch = kcontrol->private_value;
int changed = 0;
if (ch > 4) {
snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
return -EINVAL;
}
if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {
rt->output_vol[ch] = ucontrol->value.integer.value[0];
rt->ovol_updated &= ~(1 << ch);
changed = 1;
}
if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {
rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];
rt->ovol_updated &= ~(2 << ch);
changed = 1;
}
if (changed)
usb6fire_control_output_vol_update(rt);
return changed;
}
static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
unsigned int ch = kcontrol->private_value;
if (ch > 4) {
snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
return -EINVAL;
}
ucontrol->value.integer.value[0] = rt->output_vol[ch];
ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];
return 0;
}
static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
unsigned int ch = kcontrol->private_value;
u8 old = rt->output_mute;
u8 value = 0;
if (ch > 4) {
snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
return -EINVAL;
}
rt->output_mute &= ~(3 << ch);
if (ucontrol->value.integer.value[0])
value |= 1;
if (ucontrol->value.integer.value[1])
value |= 2;
rt->output_mute |= value << ch;
if (rt->output_mute != old)
usb6fire_control_output_mute_update(rt);
return rt->output_mute != old;
}
static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
unsigned int ch = kcontrol->private_value;
u8 value = rt->output_mute >> ch;
if (ch > 4) {
snd_printk(KERN_ERR PREFIX "Invalid channel in volume control.");
return -EINVAL;
}
ucontrol->value.integer.value[0] = 1 & value;
value >>= 1;
ucontrol->value.integer.value[1] = 1 & value;
return 0;
}
static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; uinfo->value.integer.min = 0;
uinfo->value.integer.max = 127; uinfo->value.integer.max = 30;
return 0; return 0;
} }
static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol, static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct control_runtime *rt = snd_kcontrol_chip(kcontrol); struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
int changed = 0; int changed = 0;
if (rt->master_vol != ucontrol->value.integer.value[0]) {
rt->master_vol = ucontrol->value.integer.value[0]; if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {
usb6fire_control_master_vol_update(rt); rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;
rt->ivol_updated &= ~(1 << 0);
changed = 1;
}
if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {
rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;
rt->ivol_updated &= ~(1 << 1);
changed = 1; changed = 1;
} }
if (changed)
usb6fire_control_input_vol_update(rt);
return changed; return changed;
} }
static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct control_runtime *rt = snd_kcontrol_chip(kcontrol); struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = rt->master_vol;
ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;
ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;
return 0; return 0;
} }
...@@ -287,16 +411,81 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, ...@@ -287,16 +411,81 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static struct __devinitdata snd_kcontrol_new elements[] = { static struct __devinitdata snd_kcontrol_new vol_elements[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Playback Volume",
.index = 0,
.private_value = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = usb6fire_control_output_vol_info,
.get = usb6fire_control_output_vol_get,
.put = usb6fire_control_output_vol_put,
.tlv = { .p = tlv_output }
},
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume", .name = "Analog Playback Volume",
.index = 1,
.private_value = 2,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = usb6fire_control_output_vol_info,
.get = usb6fire_control_output_vol_get,
.put = usb6fire_control_output_vol_put,
.tlv = { .p = tlv_output }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Playback Volume",
.index = 2,
.private_value = 4,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = usb6fire_control_output_vol_info,
.get = usb6fire_control_output_vol_get,
.put = usb6fire_control_output_vol_put,
.tlv = { .p = tlv_output }
},
{}
};
static struct __devinitdata snd_kcontrol_new mute_elements[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Playback Switch",
.index = 0, .index = 0,
.private_value = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = usb6fire_control_master_vol_info, .info = snd_ctl_boolean_stereo_info,
.get = usb6fire_control_master_vol_get, .get = usb6fire_control_output_mute_get,
.put = usb6fire_control_master_vol_put .put = usb6fire_control_output_mute_put,
}, },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Playback Switch",
.index = 1,
.private_value = 2,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_ctl_boolean_stereo_info,
.get = usb6fire_control_output_mute_get,
.put = usb6fire_control_output_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Playback Switch",
.index = 2,
.private_value = 4,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_ctl_boolean_stereo_info,
.get = usb6fire_control_output_mute_get,
.put = usb6fire_control_output_mute_put,
},
{}
};
static struct __devinitdata snd_kcontrol_new elements[] = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line/Phono Capture Route", .name = "Line/Phono Capture Route",
...@@ -324,9 +513,54 @@ static struct __devinitdata snd_kcontrol_new elements[] = { ...@@ -324,9 +513,54 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
.get = usb6fire_control_digital_thru_get, .get = usb6fire_control_digital_thru_get,
.put = usb6fire_control_digital_thru_put .put = usb6fire_control_digital_thru_put
}, },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Capture Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = usb6fire_control_input_vol_info,
.get = usb6fire_control_input_vol_get,
.put = usb6fire_control_input_vol_put,
.tlv = { .p = tlv_input }
},
{} {}
}; };
static int usb6fire_control_add_virtual(
struct control_runtime *rt,
struct snd_card *card,
char *name,
struct snd_kcontrol_new *elems)
{
int ret;
int i;
struct snd_kcontrol *vmaster =
snd_ctl_make_virtual_master(name, tlv_output);
struct snd_kcontrol *control;
if (!vmaster)
return -ENOMEM;
ret = snd_ctl_add(card, vmaster);
if (ret < 0)
return ret;
i = 0;
while (elems[i].name) {
control = snd_ctl_new1(&elems[i], rt);
if (!control)
return -ENOMEM;
ret = snd_ctl_add(card, control);
if (ret < 0)
return ret;
ret = snd_ctl_add_slave(vmaster, control);
if (ret < 0)
return ret;
i++;
}
return 0;
}
int __devinit usb6fire_control_init(struct sfire_chip *chip) int __devinit usb6fire_control_init(struct sfire_chip *chip)
{ {
int i; int i;
...@@ -352,9 +586,26 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) ...@@ -352,9 +586,26 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
usb6fire_control_opt_coax_update(rt); usb6fire_control_opt_coax_update(rt);
usb6fire_control_line_phono_update(rt); usb6fire_control_line_phono_update(rt);
usb6fire_control_master_vol_update(rt); usb6fire_control_output_vol_update(rt);
usb6fire_control_output_mute_update(rt);
usb6fire_control_input_vol_update(rt);
usb6fire_control_streaming_update(rt); usb6fire_control_streaming_update(rt);
ret = usb6fire_control_add_virtual(rt, chip->card,
"Master Playback Volume", vol_elements);
if (ret) {
snd_printk(KERN_ERR PREFIX "cannot add control.\n");
kfree(rt);
return ret;
}
ret = usb6fire_control_add_virtual(rt, chip->card,
"Master Playback Switch", mute_elements);
if (ret) {
snd_printk(KERN_ERR PREFIX "cannot add control.\n");
kfree(rt);
return ret;
}
i = 0; i = 0;
while (elements[i].name) { while (elements[i].name) {
ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -44,7 +43,11 @@ struct control_runtime { ...@@ -44,7 +43,11 @@ struct control_runtime {
bool line_phono_switch; bool line_phono_switch;
bool digital_thru_switch; bool digital_thru_switch;
bool usb_streaming; bool usb_streaming;
u8 master_vol; u8 output_vol[6];
u8 ovol_updated;
u8 output_mute;
s8 input_vol[2];
u8 ivol_updated;
}; };
int __devinit usb6fire_control_init(struct sfire_chip *chip); int __devinit usb6fire_control_init(struct sfire_chip *chip);
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* *
* Author: Torsten Schenk <torsten.schenk@zoho.com> * Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011 * Created: Jan 01, 2011
* Version: 0.3.0
* Copyright: (C) Torsten Schenk * Copyright: (C) Torsten Schenk
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
......
...@@ -106,6 +106,7 @@ config SND_USB_6FIRE ...@@ -106,6 +106,7 @@ config SND_USB_6FIRE
select BITREVERSE select BITREVERSE
select SND_RAWMIDI select SND_RAWMIDI
select SND_PCM select SND_PCM
select SND_VMASTER
help help
Say Y here to include support for TerraTec 6fire DMX USB interface. Say Y here to include support for TerraTec 6fire DMX USB interface.
......
...@@ -695,6 +695,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, ...@@ -695,6 +695,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
struct snd_usb_substream *subs) struct snd_usb_substream *subs)
{ {
struct audioformat *fp; struct audioformat *fp;
int *rate_list;
int count = 0, needs_knot = 0; int count = 0, needs_knot = 0;
int err; int err;
...@@ -708,7 +709,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, ...@@ -708,7 +709,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
if (!needs_knot) if (!needs_knot)
return 0; return 0;
subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); subs->rate_list.list = rate_list =
kmalloc(sizeof(int) * count, GFP_KERNEL);
if (!subs->rate_list.list) if (!subs->rate_list.list)
return -ENOMEM; return -ENOMEM;
subs->rate_list.count = count; subs->rate_list.count = count;
...@@ -717,7 +719,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, ...@@ -717,7 +719,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
list_for_each_entry(fp, &subs->fmt_list, list) { list_for_each_entry(fp, &subs->fmt_list, list) {
int i; int i;
for (i = 0; i < fp->nr_rates; i++) for (i = 0; i < fp->nr_rates; i++)
subs->rate_list.list[count++] = fp->rate_table[i]; rate_list[count++] = fp->rate_table[i];
} }
err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&subs->rate_list); &subs->rate_list);
......
...@@ -80,7 +80,7 @@ static int usX2Y_urb_capt_retire(struct snd_usX2Y_substream *subs) ...@@ -80,7 +80,7 @@ static int usX2Y_urb_capt_retire(struct snd_usX2Y_substream *subs)
cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset; cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
snd_printk(KERN_ERR "active frame status %i. " snd_printk(KERN_ERR "active frame status %i. "
"Most propably some hardware problem.\n", "Most probably some hardware problem.\n",
urb->iso_frame_desc[i].status); urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status; return urb->iso_frame_desc[i].status;
} }
...@@ -300,7 +300,7 @@ static void usX2Y_error_sequence(struct usX2Ydev *usX2Y, ...@@ -300,7 +300,7 @@ static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
{ {
snd_printk(KERN_ERR snd_printk(KERN_ERR
"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n" "Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
"Most propably some urb of usb-frame %i is still missing.\n" "Most probably some urb of usb-frame %i is still missing.\n"
"Cause could be too long delays in usb-hcd interrupt handling.\n", "Cause could be too long delays in usb-hcd interrupt handling.\n",
usb_get_current_frame_number(usX2Y->dev), usb_get_current_frame_number(usX2Y->dev),
subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
......
...@@ -74,7 +74,7 @@ static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs) ...@@ -74,7 +74,7 @@ static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
} }
for (i = 0; i < nr_of_packs(); i++) { for (i = 0; i < nr_of_packs(); i++) {
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status); snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status; return urb->iso_frame_desc[i].status;
} }
lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride; lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment