summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/ac97_codec.h14
-rw-r--r--sound/pci/ac97/ac97_codec.c279
-rw-r--r--sound/pci/ac97/ac97_local.h16
-rw-r--r--sound/pci/ac97/ac97_patch.c130
-rw-r--r--sound/pci/ac97/ac97_patch.h1
5 files changed, 290 insertions, 150 deletions
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index d421d964cf63..a841033c1c76 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -366,6 +366,13 @@
#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_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 */
#define AC97_RATES_FRONT_DAC 0
@@ -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_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 */
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 78d370abcbd8..c2c9d0a5466c 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -56,6 +56,7 @@ typedef struct {
const char *name;
int (*patch)(ac97_t *ac97);
int (*mpatch)(ac97_t *ac97);
+ unsigned int flags;
} ac97_codec_id_t;
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[] = {
{ 0x574d4C05, 0xffffffff, "WM9705/WM9710", patch_wolfson05, NULL},
{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL},
{ 0x574d4C12, 0xffffffff, "WM9711/WM9712", patch_wolfson11, NULL},
+{ 0x574d4c13, 0xffffffff, "WM9713/WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
{ 0x594d4800, 0xffffffff, "YMF743", NULL, NULL },
{ 0x594d4802, 0xffffffff, "YMF752", NULL, 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
* Controls
*/
-/* input mux */
-static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
- static char *texts[8] = {
- "Mic", "CD", "Video", "Aux", "Line",
- "Mix", "Mix Mono", "Phone"
- };
-
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 8;
- 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;
+ uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+ uinfo->value.enumerated.items = e->mask;
- val = snd_ac97_read_cache(ac97, AC97_REC_SEL);
- ucontrol->value.enumerated.item[0] = (val >> 8) & 7;
- ucontrol->value.enumerated.item[1] = (val >> 0) & 7;
+ if (uinfo->value.enumerated.item > e->mask - 1)
+ uinfo->value.enumerated.item = e->mask - 1;
+ strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
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);
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
unsigned short val;
- if (ucontrol->value.enumerated.item[0] > 7 ||
- ucontrol->value.enumerated.item[1] > 7)
- return -EINVAL;
- val = (ucontrol->value.enumerated.item[0] << 8) |
- (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;
-}
+ val = snd_ac97_read_cache(ac97, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+ if (e->shift_l != e->shift_r)
+ ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
-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;
}
-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);
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
unsigned short val;
- int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
+ unsigned short mask;
- if (ucontrol->value.enumerated.item[0] > 1)
+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
return -EINVAL;
- val = !!ucontrol->value.enumerated.item[0];
- if (invert)
- val = !val;
- return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift);
+ val = ucontrol->value.enumerated.item[0] << e->shift_l;
+ mask = (e->mask - 1) << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ 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 */
@@ -635,11 +581,6 @@ int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontro
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] = {
AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 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 =
AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
-static const snd_kcontrol_new_t snd_ac97_control_capture_src = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = snd_ac97_info_mux,
- .get = snd_ac97_get_mux,
- .put = snd_ac97_put_mux,
+static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
+static const char* std_3d_path[] = {"pre 3D", "post 3D"};
+static const char* std_mix[] = {"Mix", "Mic"};
+static const char* std_mic[] = {"Mic1", "Mic2"};
+
+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 =
AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);
@@ -686,12 +634,12 @@ typedef enum {
} ac97_general_index_t;
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("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 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_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0),
+AC97_ENUM("Mono Output Select", std_enum[2]),
+AC97_ENUM("Mic Select", std_enum[3]),
AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
};
@@ -1360,8 +1308,9 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
}
/* build PC Speaker controls */
- if ((ac97->flags & AC97_HAS_PC_BEEP) ||
- snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) {
+ if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
+ ((ac97->flags & AC97_HAS_PC_BEEP) ||
+ snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
for (idx = 0; idx < 2; idx++)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
return err;
@@ -1370,9 +1319,11 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
}
/* build Phone controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
- return err;
+ if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
+ if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
+ return err;
+ }
}
/* build MIC controls */
@@ -1390,15 +1341,19 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
}
/* build CD controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
- if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
- return err;
+ if (!(ac97->flags & AC97_HAS_NO_CD)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
+ if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
+ return err;
+ }
}
/* build Video controls */
- if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
- if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
- return err;
+ if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
+ if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
+ if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
+ return err;
+ }
}
/* build Aux controls */
@@ -1444,17 +1399,18 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
}
/* build Capture controls */
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
- 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)
+ if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
+ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
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 */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
for (idx = 0; idx < 2; idx++)
@@ -1672,6 +1628,18 @@ static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97)
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)
{
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)
printable(id >> 24),
printable(id >> 16),
printable(id >> 8));
- for (pid = snd_ac97_codec_id_vendors; pid->id; pid++)
- if (pid->id == (id & pid->mask)) {
- strcpy(name, pid->name);
- if (ac97) {
- if (!modem && pid->patch)
- pid->patch(ac97);
- else if (modem && pid->mpatch)
- pid->mpatch(ac97);
- }
- goto __vendor_ok;
- }
- return;
+ pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
+ if (! pid)
+ return;
- __vendor_ok:
- for (pid = snd_ac97_codec_ids; pid->id; pid++)
- if (pid->id == (id & pid->mask)) {
- strcat(name, " ");
- strcat(name, pid->name);
- if (pid->mask != 0xffffffff)
- sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
- if (ac97) {
- if (!modem && pid->patch)
- pid->patch(ac97);
- else if (modem && pid->mpatch)
- pid->mpatch(ac97);
- }
- return;
+ strcpy(name, pid->name);
+ if (ac97 && pid->patch) {
+ if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+ (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+ pid->patch(ac97);
+ }
+
+ pid = look_for_codec_id(snd_ac97_codec_ids, id);
+ if (pid) {
+ strcat(name, " ");
+ strcat(name, pid->name);
+ if (pid->mask != 0xffffffff)
+ sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+ 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)
char name[64];
unsigned long end_time;
unsigned int reg;
+ const ac97_codec_id_t *pid;
static snd_device_ops_t ops = {
.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)
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 */
if (bus->ops->wait)
bus->ops->wait(ac97);
@@ -1926,6 +1898,9 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
snd_ac97_free(ac97);
return -EIO;
}
+ pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+ if (pid)
+ ac97->flags |= pid->flags;
/* test for AC'97 */
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)
if (ac97_is_audio(ac97)) {
/* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
- snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
- udelay(100);
+ if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+ 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 */
- snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
end_time = jiffies + (HZ / 10);
do {
@@ -2234,9 +2211,11 @@ void snd_ac97_resume(ac97_t *ac97)
}
snd_ac97_write(ac97, AC97_POWERDOWN, 0);
- snd_ac97_write(ac97, AC97_RESET, 0);
- udelay(100);
- snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+ if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+ snd_ac97_write(ac97, AC97_RESET, 0);
+ udelay(100);
+ snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+ }
snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h
index fe41f9c3d818..536a4d4793af 100644
--- a/sound/pci/ac97/ac97_local.h
+++ b/sound/pci/ac97/ac97_local.h
@@ -32,6 +32,19 @@
{ .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 = 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 */
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 *
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_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,
unsigned short mask, unsigned short value);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 3df9bef91f1e..65e6b2149d86 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -305,6 +305,136 @@ int patch_wolfson11(ac97_t * ac97)
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
*/
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 641b0be889ea..6db51c96f5d0 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -28,6 +28,7 @@ int patch_wolfson03(ac97_t * ac97);
int patch_wolfson04(ac97_t * ac97);
int patch_wolfson05(ac97_t * ac97);
int patch_wolfson11(ac97_t * ac97);
+int patch_wolfson13(ac97_t * ac97);
int patch_tritech_tr28028(ac97_t * ac97);
int patch_sigmatel_stac9700(ac97_t * ac97);
int patch_sigmatel_stac9708(ac97_t * ac97);