* [PATCH 1/4] AC97 wm9713 support
@ 2005-02-25 14:23 Liam Girdwood
2005-03-10 11:43 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Liam Girdwood @ 2005-02-25 14:23 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 1076 bytes --]
This patch is against ac97_codec.c and includes parts of Takashi's AC97
low power codec patch posted to lkml on Jan 4th 2005. It also includes
better enumerated mixer type support.
Apologies for the size of this patch. This was the minimum changes
required to get audio in/out of the codec.
Changes:-
o Added WM9713/WM9714 to codec ID table
o Removed snd_ac97_[info|get|put]_mux().
o Added snd_ac97_[info|get|put]_enum_single() for single channel enums
o Added snd_ac97_[info|get|put]_enum_double() for double channel enums
o Moved AC97_ENUM_DOUBLE macro to ac97_local.h
o Moved AC97_DOUBLE macro to ac97_local.h
o Changed existing std enums to use new enum macros
o Added the following AC97 flags to be checked at setup:-
AC97_HAS_NO_PC_BEEP, AC97_HAS_NO_PHONE, AC97_HAS_NO_CD,
AC97_HAS_NO_VIDEO, AC97_HAS_NO_REC_GAIN.
o Added look_for_codec_id() to match given id with the codec id.
o Added register reset protection for codecs that need it.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
[-- Attachment #2: ac97_codec.c.diff --]
[-- Type: text/x-patch, Size: 15896 bytes --]
diff -u -r1.172 ac97_codec.c
--- pci/ac97/ac97_codec.c 16 Feb 2005 18:27:12 -0000 1.172
+++ pci/ac97/ac97_codec.c 24 Feb 2005 16:49:55 -0000
@@ -56,6 +56,7 @@
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[] = {
@@ -161,6 +162,7 @@
{ 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, WM9713_AC97_FLAGS},
{ 0x594d4800, 0xffffffff, "YMF743", NULL, NULL },
{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL },
{ 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL },
@@ -441,108 +443,85 @@
* Controls
*/
-/* input mux */
-static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+int snd_ac97_info_enum_single(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]);
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = e->mask;
+
+ 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_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+int snd_ac97_get_enum_single(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;
-
- 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;
+
+ val = snd_ac97_read_cache(ac97, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+
return 0;
}
-static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+int snd_ac97_put_enum_single(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)
+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
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;
+ val = (ucontrol->value.enumerated.item[0] << e->shift_l);
+ return snd_ac97_update_bits(ac97, e->reg, (e->mask - 1) << e->shift_l, val);
+}
- switch (reg) {
- case AC97_GENERAL_PURPOSE:
- switch (shift) {
- case 15: texts = texts1; break;
- case 9: texts = texts2; break;
- case 8: texts = texts3; break;
- }
- }
+int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+
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]);
+ uinfo->count = 2;
+ uinfo->value.enumerated.items = e->mask;
+
+ 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_get_enum_double(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;
- 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;
+ val = snd_ac97_read_cache(ac97, e->reg);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+ ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
+
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 = ((e->mask - 1) << e->shift_l) |
+ ((e->mask - 1) << e->shift_r);
- if (ucontrol->value.enumerated.item[0] > 1)
+ if ((ucontrol->value.enumerated.item[0] > e->mask - 1) ||
+ (ucontrol->value.enumerated.item[1] > 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) |
+ (ucontrol->value.enumerated.item[1] << e->shift_r);
+ return snd_ac97_update_bits(ac97, e->reg, mask, val);
}
/* save/restore ac97 v2.3 paging */
@@ -634,11 +613,6 @@
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)
@@ -658,14 +632,21 @@
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(AC97_REC_SEL, 8, 0, 8, std_rec_sel),
+AC97_ENUM(AC97_GENERAL_PURPOSE, 15, 0, 2, std_3d_path),
+AC97_ENUM(AC97_GENERAL_PURPOSE, 9, 0, 2, std_mix),
+AC97_ENUM(AC97_GENERAL_PURPOSE, 8, 0, 2, std_mic),
};
+static const snd_kcontrol_new_t snd_ac97_control_capture_src =
+AC97_ENUM_DOUBLE("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);
@@ -685,12 +666,12 @@
} 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_SINGLE("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_SINGLE("Mono Output Select", std_enum[2]),
+AC97_ENUM_SINGLE("Mic Select", std_enum[3]),
AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
};
@@ -1356,8 +1337,9 @@
}
/* 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;
@@ -1366,9 +1348,11 @@
}
/* 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 */
@@ -1386,15 +1370,19 @@
}
/* 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 */
@@ -1440,17 +1428,18 @@
}
/* 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++)
@@ -1662,6 +1651,18 @@
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;
@@ -1670,35 +1671,30 @@
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);
}
/**
@@ -1840,6 +1836,7 @@
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,
};
@@ -1890,6 +1887,14 @@
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);
@@ -1916,6 +1921,9 @@
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)) {
@@ -1954,10 +1962,12 @@
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 {
@@ -2224,9 +2234,11 @@
}
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]);
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/4] AC97 wm9713 support
2005-02-25 14:23 [PATCH 1/4] AC97 wm9713 support Liam Girdwood
@ 2005-03-10 11:43 ` Takashi Iwai
2005-03-10 13:26 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2005-03-10 11:43 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel
At Fri, 25 Feb 2005 14:23:17 +0000,
Liam Girdwood wrote:
>
> [1 <text/plain (7bit)>]
> This patch is against ac97_codec.c and includes parts of Takashi's AC97
> low power codec patch posted to lkml on Jan 4th 2005. It also includes
> better enumerated mixer type support.
>
> Apologies for the size of this patch. This was the minimum changes
> required to get audio in/out of the codec.
>
> Changes:-
>
> o Added WM9713/WM9714 to codec ID table
> o Removed snd_ac97_[info|get|put]_mux().
> o Added snd_ac97_[info|get|put]_enum_single() for single channel enums
> o Added snd_ac97_[info|get|put]_enum_double() for double channel enums
> o Moved AC97_ENUM_DOUBLE macro to ac97_local.h
> o Moved AC97_DOUBLE macro to ac97_local.h
How about to create unified enum callbacks for both single and double
channels? For example, in get/put callback, if shift_l == shift_r,
it's handled as a single control, otherwise double.
Takashi
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/4] AC97 wm9713 support
2005-03-10 11:43 ` Takashi Iwai
@ 2005-03-10 13:26 ` Takashi Iwai
2005-03-10 14:13 ` Liam Girdwood
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2005-03-10 13:26 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 1560 bytes --]
At Thu, 10 Mar 2005 12:43:46 +0100,
I wrote:
>
> At Fri, 25 Feb 2005 14:23:17 +0000,
> Liam Girdwood wrote:
> >
> > [1 <text/plain (7bit)>]
> > This patch is against ac97_codec.c and includes parts of Takashi's AC97
> > low power codec patch posted to lkml on Jan 4th 2005. It also includes
> > better enumerated mixer type support.
> >
> > Apologies for the size of this patch. This was the minimum changes
> > required to get audio in/out of the codec.
> >
> > Changes:-
> >
> > o Added WM9713/WM9714 to codec ID table
> > o Removed snd_ac97_[info|get|put]_mux().
> > o Added snd_ac97_[info|get|put]_enum_single() for single channel enums
> > o Added snd_ac97_[info|get|put]_enum_double() for double channel enums
> > o Moved AC97_ENUM_DOUBLE macro to ac97_local.h
> > o Moved AC97_DOUBLE macro to ac97_local.h
>
> How about to create unified enum callbacks for both single and double
> channels? For example, in get/put callback, if shift_l == shift_r,
> it's handled as a single control, otherwise double.
The attached is the changed patch:
- only snd_ac97_*_enum_double(), no single callbacks
- change the definition of AC97_* macro:
AC97_ENUM_DOUBLE() and AC97_ENUM_SINGLE() defines the enum struct
for double and single channels.
AC97_ENUM() defines the control struct.
(IMO, this is more straightforward.)
- WM-specific flags are set in the patch. Only AC97_DEFAULT_POWER_OFF
is set in ac97_codec.c, which has to be known before the patch.
- fix compilation warnings.
Liam, could you check whether it's OK?
Takashi
[-- Attachment #2: Type: text/plain, Size: 24793 bytes --]
Index: alsa-kernel/include/ac97_codec.h
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/include/ac97_codec.h,v
retrieving revision 1.62
diff -u -r1.62 ac97_codec.h
--- alsa-kernel/include/ac97_codec.h 11 Jan 2005 17:43:15 -0000 1.62
+++ alsa-kernel/include/ac97_codec.h 10 Mar 2005 13:24:18 -0000
@@ -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_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 */
Index: alsa-kernel/pci/ac97/ac97_codec.c
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/pci/ac97/ac97_codec.c,v
retrieving revision 1.169
diff -u -r1.169 ac97_codec.c
--- alsa-kernel/pci/ac97/ac97_codec.c 8 Mar 2005 16:19:28 -0000 1.169
+++ alsa-kernel/pci/ac97/ac97_codec.c 10 Mar 2005 13:24:18 -0000
@@ -56,6 +56,7 @@
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 @@
{ 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 @@
* 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;
+ 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);
- 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)
+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;
- 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)
-{
- 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;
-
- 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 @@
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 @@
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 @@
} 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 @@
}
/* 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 @@
}
/* 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 @@
}
/* 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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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]);
Index: alsa-kernel/pci/ac97/ac97_local.h
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/pci/ac97/ac97_local.h,v
retrieving revision 1.12
diff -u -r1.12 ac97_local.h
--- alsa-kernel/pci/ac97/ac97_local.h 19 Jan 2005 12:00:35 -0000 1.12
+++ alsa-kernel/pci/ac97/ac97_local.h 10 Mar 2005 13:24:18 -0000
@@ -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 @@
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);
Index: alsa-kernel/pci/ac97/ac97_patch.c
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/pci/ac97/ac97_patch.c,v
retrieving revision 1.77
diff -u -r1.77 ac97_patch.c
--- alsa-kernel/pci/ac97/ac97_patch.c 23 Feb 2005 14:01:37 -0000 1.77
+++ alsa-kernel/pci/ac97/ac97_patch.c 10 Mar 2005 13:24:18 -0000
@@ -305,6 +305,136 @@
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
*/
Index: alsa-kernel/pci/ac97/ac97_patch.h
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/pci/ac97/ac97_patch.h,v
retrieving revision 1.19
diff -u -r1.19 ac97_patch.h
--- alsa-kernel/pci/ac97/ac97_patch.h 16 Feb 2005 18:25:30 -0000 1.19
+++ alsa-kernel/pci/ac97/ac97_patch.h 10 Mar 2005 13:24:18 -0000
@@ -28,6 +28,7 @@
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);
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/4] AC97 wm9713 support
2005-03-10 13:26 ` Takashi Iwai
@ 2005-03-10 14:13 ` Liam Girdwood
2005-03-10 14:36 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Liam Girdwood @ 2005-03-10 14:13 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
On Thu, 2005-03-10 at 13:26, Takashi Iwai wrote:
>
> The attached is the changed patch:
>
> - only snd_ac97_*_enum_double(), no single callbacks
> - change the definition of AC97_* macro:
> AC97_ENUM_DOUBLE() and AC97_ENUM_SINGLE() defines the enum struct
> for double and single channels.
> AC97_ENUM() defines the control struct.
> (IMO, this is more straightforward.)
> - WM-specific flags are set in the patch. Only AC97_DEFAULT_POWER_OFF
> is set in ac97_codec.c, which has to be known before the patch.
> - fix compilation warnings.
>
>
> Liam, could you check whether it's OK?
>
Works perfectly·
Thanks
Liam
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_ide95&alloc_id\x14396&op=click
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/4] AC97 wm9713 support
2005-03-10 14:13 ` Liam Girdwood
@ 2005-03-10 14:36 ` Takashi Iwai
0 siblings, 0 replies; 5+ messages in thread
From: Takashi Iwai @ 2005-03-10 14:36 UTC (permalink / raw)
To: Liam Girdwood; +Cc: alsa-devel
At Thu, 10 Mar 2005 14:13:12 +0000,
Liam Girdwood wrote:
>
> On Thu, 2005-03-10 at 13:26, Takashi Iwai wrote:
>
> >
> > The attached is the changed patch:
> >
> > - only snd_ac97_*_enum_double(), no single callbacks
> > - change the definition of AC97_* macro:
> > AC97_ENUM_DOUBLE() and AC97_ENUM_SINGLE() defines the enum struct
> > for double and single channels.
> > AC97_ENUM() defines the control struct.
> > (IMO, this is more straightforward.)
> > - WM-specific flags are set in the patch. Only AC97_DEFAULT_POWER_OFF
> > is set in ac97_codec.c, which has to be known before the patch.
> > - fix compilation warnings.
> >
> >
> > Liam, could you check whether it's OK?
> >
>
> Works perfectly·
Thanks for the confirmation. Now applied to CVS.
Takashi
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_ide95&alloc_id\x14396&op=click
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2005-03-10 14:36 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-25 14:23 [PATCH 1/4] AC97 wm9713 support Liam Girdwood
2005-03-10 11:43 ` Takashi Iwai
2005-03-10 13:26 ` Takashi Iwai
2005-03-10 14:13 ` Liam Girdwood
2005-03-10 14:36 ` Takashi Iwai
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.