Commit e7e3950a authored by Jaroslav Kysela's avatar Jaroslav Kysela

Merge suse.cz:/home/perex/bk/linux-sound/linux-sound

into suse.cz:/home/perex/bk/linux-sound/work
parents ece8df79 4eaf05ae
...@@ -366,6 +366,13 @@ ...@@ -366,6 +366,13 @@
#define AC97_DOUBLE_RATE (1<<5) /* supports double rate playback */ #define AC97_DOUBLE_RATE (1<<5) /* supports double rate playback */
#define AC97_HAS_NO_MASTER_VOL (1<<6) /* no Master volume */ #define AC97_HAS_NO_MASTER_VOL (1<<6) /* no Master volume */
#define AC97_HAS_NO_PCM_VOL (1<<7) /* no PCM volume */ #define AC97_HAS_NO_PCM_VOL (1<<7) /* no PCM volume */
#define AC97_DEFAULT_POWER_OFF (1<<8) /* no RESET write */
#define AC97_MODEM_PATCH (1<<9) /* modem patch */
#define AC97_HAS_NO_REC_GAIN (1<<10) /* no Record gain */
#define AC97_HAS_NO_PHONE (1<<11) /* no PHONE volume */
#define AC97_HAS_NO_PC_BEEP (1<<12) /* no PC Beep volume */
#define AC97_HAS_NO_VIDEO (1<<13) /* no Video volume */
#define AC97_HAS_NO_CD (1<<14) /* no CD volume */
/* rates indexes */ /* rates indexes */
#define AC97_RATES_FRONT_DAC 0 #define AC97_RATES_FRONT_DAC 0
...@@ -580,4 +587,11 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, ...@@ -580,4 +587,11 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
int snd_ac97_pcm_close(struct ac97_pcm *pcm); int snd_ac97_pcm_close(struct ac97_pcm *pcm);
int snd_ac97_pcm_double_rate_rules(snd_pcm_runtime_t *runtime); int snd_ac97_pcm_double_rate_rules(snd_pcm_runtime_t *runtime);
struct ac97_enum {
unsigned char reg;
unsigned char shift_l;
unsigned char shift_r;
unsigned short mask;
const char **texts;
};
#endif /* __SOUND_AC97_CODEC_H */ #endif /* __SOUND_AC97_CODEC_H */
...@@ -56,6 +56,7 @@ typedef struct { ...@@ -56,6 +56,7 @@ typedef struct {
const char *name; const char *name;
int (*patch)(ac97_t *ac97); int (*patch)(ac97_t *ac97);
int (*mpatch)(ac97_t *ac97); int (*mpatch)(ac97_t *ac97);
unsigned int flags;
} ac97_codec_id_t; } ac97_codec_id_t;
static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = { static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {
...@@ -162,6 +163,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { ...@@ -162,6 +163,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
{ 0x574d4C05, 0xffffffff, "WM9705/WM9710", patch_wolfson05, NULL}, { 0x574d4C05, 0xffffffff, "WM9705/WM9710", patch_wolfson05, NULL},
{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL}, { 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL},
{ 0x574d4C12, 0xffffffff, "WM9711/WM9712", patch_wolfson11, NULL}, { 0x574d4C12, 0xffffffff, "WM9711/WM9712", patch_wolfson11, NULL},
{ 0x574d4c13, 0xffffffff, "WM9713/WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
{ 0x594d4800, 0xffffffff, "YMF743", NULL, NULL }, { 0x594d4800, 0xffffffff, "YMF743", NULL, NULL },
{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL }, { 0x594d4802, 0xffffffff, "YMF752", NULL, NULL },
{ 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL }, { 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL },
...@@ -442,108 +444,52 @@ static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned sho ...@@ -442,108 +444,52 @@ static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned sho
* Controls * Controls
*/ */
/* input mux */ int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{ {
static char *texts[8] = { struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
"Mic", "CD", "Video", "Aux", "Line",
"Mix", "Mix Mono", "Phone"
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 2; uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
uinfo->value.enumerated.items = 8; uinfo->value.enumerated.items = e->mask;
if (uinfo->value.enumerated.item > 7)
uinfo->value.enumerated.item = 7;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
val = snd_ac97_read_cache(ac97, AC97_REC_SEL); if (uinfo->value.enumerated.item > e->mask - 1)
ucontrol->value.enumerated.item[0] = (val >> 8) & 7; uinfo->value.enumerated.item = e->mask - 1;
ucontrol->value.enumerated.item[1] = (val >> 0) & 7; strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
return 0; return 0;
} }
static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{ {
ac97_t *ac97 = snd_kcontrol_chip(kcontrol); ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
unsigned short val; unsigned short val;
if (ucontrol->value.enumerated.item[0] > 7 || val = snd_ac97_read_cache(ac97, e->reg);
ucontrol->value.enumerated.item[1] > 7) ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
return -EINVAL; if (e->shift_l != e->shift_r)
val = (ucontrol->value.enumerated.item[0] << 8) | ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
(ucontrol->value.enumerated.item[1] << 0);
return snd_ac97_update(ac97, AC97_REC_SEL, val);
}
/* standard stereo enums */
#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \
.get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \
.private_value = reg | (shift << 8) | (invert << 24) }
static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
static char *texts1[2] = { "pre 3D", "post 3D" };
static char *texts2[2] = { "Mix", "Mic" };
static char *texts3[2] = { "Mic1", "Mic2" };
char **texts = NULL;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
switch (reg) {
case AC97_GENERAL_PURPOSE:
switch (shift) {
case 15: texts = texts1; break;
case 9: texts = texts2; break;
case 8: texts = texts3; break;
}
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item > 1)
uinfo->value.enumerated.item = 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
val = (snd_ac97_read_cache(ac97, reg) >> shift) & 1;
if (invert)
val ^= 1;
ucontrol->value.enumerated.item[0] = val;
return 0; return 0;
} }
static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{ {
ac97_t *ac97 = snd_kcontrol_chip(kcontrol); ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
unsigned short val; unsigned short val;
int reg = kcontrol->private_value & 0xff; unsigned short mask;
int shift = (kcontrol->private_value >> 8) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
if (ucontrol->value.enumerated.item[0] > 1) if (ucontrol->value.enumerated.item[0] > e->mask - 1)
return -EINVAL; return -EINVAL;
val = !!ucontrol->value.enumerated.item[0]; val = ucontrol->value.enumerated.item[0] << e->shift_l;
if (invert) mask = (e->mask - 1) << e->shift_l;
val = !val; if (e->shift_l != e->shift_r) {
return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift); if (ucontrol->value.enumerated.item[1] > e->mask - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
mask |= (e->mask - 1) << e->shift_r;
}
return snd_ac97_update_bits(ac97, e->reg, mask, val);
} }
/* save/restore ac97 v2.3 paging */ /* save/restore ac97 v2.3 paging */
...@@ -635,11 +581,6 @@ int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontro ...@@ -635,11 +581,6 @@ int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontro
return err; return err;
} }
#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \
.get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = {
AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1) AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
...@@ -659,14 +600,21 @@ static const snd_kcontrol_new_t snd_ac97_controls_mic_boost = ...@@ -659,14 +600,21 @@ static const snd_kcontrol_new_t snd_ac97_controls_mic_boost =
AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0); AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
static const snd_kcontrol_new_t snd_ac97_control_capture_src = { static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, static const char* std_3d_path[] = {"pre 3D", "post 3D"};
.name = "Capture Source", static const char* std_mix[] = {"Mix", "Mic"};
.info = snd_ac97_info_mux, static const char* std_mic[] = {"Mic1", "Mic2"};
.get = snd_ac97_get_mux,
.put = snd_ac97_put_mux, static const struct ac97_enum std_enum[] = {
AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel),
AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path),
AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix),
AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic),
}; };
static const snd_kcontrol_new_t snd_ac97_control_capture_src =
AC97_ENUM("Capture Source", std_enum[0]);
static const snd_kcontrol_new_t snd_ac97_control_capture_vol = static const snd_kcontrol_new_t snd_ac97_control_capture_vol =
AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0); AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);
...@@ -686,12 +634,12 @@ typedef enum { ...@@ -686,12 +634,12 @@ typedef enum {
} ac97_general_index_t; } ac97_general_index_t;
static const snd_kcontrol_new_t snd_ac97_controls_general[7] = { static const snd_kcontrol_new_t snd_ac97_controls_general[7] = {
AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0), AC97_ENUM("PCM Out Path & Mute", std_enum[1]),
AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0), AC97_ENUM("Mono Output Select", std_enum[2]),
AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0), AC97_ENUM("Mic Select", std_enum[3]),
AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
}; };
...@@ -1360,8 +1308,9 @@ static int snd_ac97_mixer_build(ac97_t * ac97) ...@@ -1360,8 +1308,9 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
} }
/* build PC Speaker controls */ /* build PC Speaker controls */
if ((ac97->flags & AC97_HAS_PC_BEEP) || if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) { ((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
return err; return err;
...@@ -1370,9 +1319,11 @@ static int snd_ac97_mixer_build(ac97_t * ac97) ...@@ -1370,9 +1319,11 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
} }
/* build Phone controls */ /* build Phone controls */
if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0) if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
return err; if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
return err;
}
} }
/* build MIC controls */ /* build MIC controls */
...@@ -1390,15 +1341,19 @@ static int snd_ac97_mixer_build(ac97_t * ac97) ...@@ -1390,15 +1341,19 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
} }
/* build CD controls */ /* build CD controls */
if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { if (!(ac97->flags & AC97_HAS_NO_CD)) {
if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0) if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
return err; if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
return err;
}
} }
/* build Video controls */ /* build Video controls */
if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0) if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
return err; if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
return err;
}
} }
/* build Aux controls */ /* build Aux controls */
...@@ -1444,17 +1399,18 @@ static int snd_ac97_mixer_build(ac97_t * ac97) ...@@ -1444,17 +1399,18 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
} }
/* build Capture controls */ /* build Capture controls */
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
return err; if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0)
return err; return err;
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0)
return err;
}
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
return err;
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
} }
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
return err;
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
/* build MIC Capture controls */ /* build MIC Capture controls */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
for (idx = 0; idx < 2; idx++) for (idx = 0; idx < 2; idx++)
...@@ -1672,6 +1628,18 @@ static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97) ...@@ -1672,6 +1628,18 @@ static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97)
return result; return result;
} }
/* look for the codec id table matching with the given id */
static const ac97_codec_id_t *look_for_codec_id(const ac97_codec_id_t *table,
unsigned int id)
{
const ac97_codec_id_t *pid;
for (pid = table; pid->id; pid++)
if (pid->id == (id & pid->mask))
return pid;
return NULL;
}
void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem) void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem)
{ {
const ac97_codec_id_t *pid; const ac97_codec_id_t *pid;
...@@ -1680,35 +1648,30 @@ void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem) ...@@ -1680,35 +1648,30 @@ void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem)
printable(id >> 24), printable(id >> 24),
printable(id >> 16), printable(id >> 16),
printable(id >> 8)); printable(id >> 8));
for (pid = snd_ac97_codec_id_vendors; pid->id; pid++) pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
if (pid->id == (id & pid->mask)) { if (! pid)
strcpy(name, pid->name); return;
if (ac97) {
if (!modem && pid->patch)
pid->patch(ac97);
else if (modem && pid->mpatch)
pid->mpatch(ac97);
}
goto __vendor_ok;
}
return;
__vendor_ok: strcpy(name, pid->name);
for (pid = snd_ac97_codec_ids; pid->id; pid++) if (ac97 && pid->patch) {
if (pid->id == (id & pid->mask)) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
strcat(name, " "); (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
strcat(name, pid->name); pid->patch(ac97);
if (pid->mask != 0xffffffff) }
sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
if (ac97) { pid = look_for_codec_id(snd_ac97_codec_ids, id);
if (!modem && pid->patch) if (pid) {
pid->patch(ac97); strcat(name, " ");
else if (modem && pid->mpatch) strcat(name, pid->name);
pid->mpatch(ac97); if (pid->mask != 0xffffffff)
} sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
return; if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
} }
sprintf(name + strlen(name), " id %x", id & 0xff); } else
sprintf(name + strlen(name), " id %x", id & 0xff);
} }
/** /**
...@@ -1850,6 +1813,7 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) ...@@ -1850,6 +1813,7 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
char name[64]; char name[64];
unsigned long end_time; unsigned long end_time;
unsigned int reg; unsigned int reg;
const ac97_codec_id_t *pid;
static snd_device_ops_t ops = { static snd_device_ops_t ops = {
.dev_free = snd_ac97_dev_free, .dev_free = snd_ac97_dev_free,
}; };
...@@ -1900,6 +1864,14 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) ...@@ -1900,6 +1864,14 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
goto __access_ok; goto __access_ok;
} }
ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
if (ac97->id && ac97->id != (unsigned int)-1) {
pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF))
goto __access_ok;
}
snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */
if (bus->ops->wait) if (bus->ops->wait)
bus->ops->wait(ac97); bus->ops->wait(ac97);
...@@ -1926,6 +1898,9 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) ...@@ -1926,6 +1898,9 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
snd_ac97_free(ac97); snd_ac97_free(ac97);
return -EIO; return -EIO;
} }
pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
if (pid)
ac97->flags |= pid->flags;
/* test for AC'97 */ /* test for AC'97 */
if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) { if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
...@@ -1964,10 +1939,12 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) ...@@ -1964,10 +1939,12 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
if (ac97_is_audio(ac97)) { if (ac97_is_audio(ac97)) {
/* nothing should be in powerdown mode */ /* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */ if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
udelay(100); snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
udelay(100);
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
}
/* nothing should be in powerdown mode */ /* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0); snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
end_time = jiffies + (HZ / 10); end_time = jiffies + (HZ / 10);
do { do {
...@@ -2234,9 +2211,11 @@ void snd_ac97_resume(ac97_t *ac97) ...@@ -2234,9 +2211,11 @@ void snd_ac97_resume(ac97_t *ac97)
} }
snd_ac97_write(ac97, AC97_POWERDOWN, 0); snd_ac97_write(ac97, AC97_POWERDOWN, 0);
snd_ac97_write(ac97, AC97_RESET, 0); if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
udelay(100); snd_ac97_write(ac97, AC97_RESET, 0);
snd_ac97_write(ac97, AC97_POWERDOWN, 0); udelay(100);
snd_ac97_write(ac97, AC97_POWERDOWN, 0);
}
snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
......
...@@ -32,6 +32,19 @@ ...@@ -32,6 +32,19 @@
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \
.get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
.private_value = AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) } .private_value = AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) }
#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \
.get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.mask = xmask, .texts = xtexts }
#define AC97_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
AC97_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
#define AC97_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \
.get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \
.private_value = (unsigned long)&xenum }
/* ac97_codec.c */ /* ac97_codec.c */
extern const char *snd_ac97_stereo_enhancements[]; extern const char *snd_ac97_stereo_enhancements[];
...@@ -49,6 +62,9 @@ int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char * ...@@ -49,6 +62,9 @@ int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char *
void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst); void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst);
void snd_ac97_restore_status(ac97_t *ac97); void snd_ac97_restore_status(ac97_t *ac97);
void snd_ac97_restore_iec958(ac97_t *ac97); void snd_ac97_restore_iec958(ac97_t *ac97);
int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg,
unsigned short mask, unsigned short value); unsigned short mask, unsigned short value);
......
...@@ -305,6 +305,136 @@ int patch_wolfson11(ac97_t * ac97) ...@@ -305,6 +305,136 @@ int patch_wolfson11(ac97_t * ac97)
return 0; return 0;
} }
static const char* wm9713_mic_mixer[] = {"Stereo", "Mic1", "Mic2", "Mute"};
static const char* wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
static const char* wm9713_rec_src_l[] = {"Mic1", "Mic2", "Line L", "Mono In", "HP Mix L", "Spk Mix", "Mono Mix", "Zh"};
static const char* wm9713_rec_src_r[] = {"Mic1", "Mic2", "Line R", "Mono In", "HP Mix R", "Spk Mix", "Mono Mix", "Zh"};
static const struct ac97_enum wm9713_enum[] = {
AC97_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer),
AC97_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux),
AC97_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux),
AC97_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src_l),
AC97_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src_r),
};
static const snd_kcontrol_new_t wm13_snd_ac97_controls_line_in[] = {
AC97_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
AC97_SINGLE("Line In to Headphone Mute", AC97_PC_BEEP, 15, 1, 1),
AC97_SINGLE("Line In to Speaker Mute", AC97_PC_BEEP, 14, 1, 1),
AC97_SINGLE("Line In to Mono Mute", AC97_PC_BEEP, 13, 1, 1),
};
static const snd_kcontrol_new_t wm13_snd_ac97_controls_dac[] = {
AC97_DOUBLE("DAC Volume", AC97_PHONE, 8, 0, 31, 1),
AC97_SINGLE("DAC to Headphone Mute", AC97_PHONE, 15, 1, 1),
AC97_SINGLE("DAC to Speaker Mute", AC97_PHONE, 14, 1, 1),
AC97_SINGLE("DAC to Mono Mute", AC97_PHONE, 13, 1, 1),
};
static const snd_kcontrol_new_t wm13_snd_ac97_controls_mic[] = {
AC97_SINGLE("MICA Volume", AC97_MIC, 8, 31, 1),
AC97_SINGLE("MICB Volume", AC97_MIC, 0, 31, 1),
AC97_SINGLE("MICA to Mono Mute", AC97_LINE, 7, 1, 1),
AC97_SINGLE("MICB to Mono Mute", AC97_LINE, 6, 1, 1),
AC97_SINGLE("MIC Boost (+20dB)", AC97_LINE, 5, 1, 1),
AC97_ENUM("MIC Headphone Routing", wm9713_enum[0]),
AC97_SINGLE("MIC Headphone Mixer Volume", AC97_LINE, 0, 7, 1)
};
static const snd_kcontrol_new_t wm13_snd_ac97_controls_adc[] = {
AC97_SINGLE("ADC Mute", AC97_CD, 15, 1, 1),
AC97_DOUBLE("Gain Step Size (1.5dB/0.75dB)", AC97_CD, 14, 6, 1, 1),
AC97_DOUBLE("ADC Volume",AC97_CD, 8, 0, 15, 0),
AC97_SINGLE("ADC Zero Cross", AC97_CD, 7, 1, 1),
};
static const snd_kcontrol_new_t wm13_snd_ac97_controls_recsel[] = {
AC97_ENUM("Record to Headphone Path", wm9713_enum[1]),
AC97_SINGLE("Record to Headphone Volume", AC97_VIDEO, 11, 7, 0),
AC97_ENUM("Record to Mono Path", wm9713_enum[2]),
AC97_SINGLE("Record to Mono Boost (+20dB)", AC97_VIDEO, 8, 1, 0),
AC97_SINGLE("Record ADC Boost (+20dB)", AC97_VIDEO, 6, 1, 0),
AC97_ENUM("Record Select Left", wm9713_enum[3]),
AC97_ENUM("Record Select Right", wm9713_enum[4]),
};
static int patch_wolfson_wm9713_specific(ac97_t * ac97)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_line_in); i++) {
if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_line_in[i], ac97))) < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_dac); i++) {
if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_dac[i], ac97))) < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_PHONE, 0x0808);
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_mic); i++) {
if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_mic[i], ac97))) < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_MIC, 0x0808);
snd_ac97_write_cache(ac97, AC97_LINE, 0x00da);
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_adc); i++) {
if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_adc[i], ac97))) < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_CD, 0x0808);
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_recsel); i++) {
if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_recsel[i], ac97))) < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_VIDEO, 0xd612);
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x1ba0);
return 0;
}
#ifdef CONFIG_PM
static void patch_wolfson_wm9713_suspend (ac97_t * ac97)
{
snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xfeff);
snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xffff);
}
static void patch_wolfson_wm9713_resume (ac97_t * ac97)
{
snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
}
#endif
static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
.build_specific = patch_wolfson_wm9713_specific,
#ifdef CONFIG_PM
.suspend = patch_wolfson_wm9713_suspend,
.resume = patch_wolfson_wm9713_resume
#endif
};
int patch_wolfson13(ac97_t * ac97)
{
ac97->build_ops = &patch_wolfson_wm9713_ops;
ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_PHONE |
AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD;
snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
return 0;
}
/* /*
* Tritech codec * Tritech codec
*/ */
......
...@@ -28,6 +28,7 @@ int patch_wolfson03(ac97_t * ac97); ...@@ -28,6 +28,7 @@ int patch_wolfson03(ac97_t * ac97);
int patch_wolfson04(ac97_t * ac97); int patch_wolfson04(ac97_t * ac97);
int patch_wolfson05(ac97_t * ac97); int patch_wolfson05(ac97_t * ac97);
int patch_wolfson11(ac97_t * ac97); int patch_wolfson11(ac97_t * ac97);
int patch_wolfson13(ac97_t * ac97);
int patch_tritech_tr28028(ac97_t * ac97); int patch_tritech_tr28028(ac97_t * ac97);
int patch_sigmatel_stac9700(ac97_t * ac97); int patch_sigmatel_stac9700(ac97_t * ac97);
int patch_sigmatel_stac9708(ac97_t * ac97); int patch_sigmatel_stac9708(ac97_t * ac97);
......
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