diff options
Diffstat (limited to 'sound/pci')
72 files changed, 10260 insertions, 1879 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 428efdb..6d7a00f 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -274,6 +274,19 @@ config SND_HDSP To compile this driver as a module, choose M here: the module will be called snd-hdsp. +config SND_HDSPM + tristate "RME Hammerfall DSP MADI" + depends on SND + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM + help + Say Y here to include support for RME Hammerfall DSP MADI + soundcards. + + To compile this driver as a module, choose M here: the module + will be called snd-hdspm. + config SND_TRIDENT tristate "Trident 4D-Wave DX/NX; SiS 7018" depends on SND diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 0b024ec..a4b72cd 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -120,6 +120,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x414c4770, 0xfffffff0, "ALC203", NULL, NULL }, { 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL }, { 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL }, +{ 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL }, { 0x434d4978, 0xffffffff, "CMI9761", patch_cm9761, NULL }, { 0x434d4982, 0xffffffff, "CMI9761", patch_cm9761, NULL }, { 0x434d4983, 0xffffffff, "CMI9761", patch_cm9761, NULL }, @@ -149,7 +150,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x4e534331, 0xffffffff, "LM4549", NULL, NULL }, { 0x4e534350, 0xffffffff, "LM4550", NULL, NULL }, { 0x50534304, 0xffffffff, "UCB1400", NULL, NULL }, -{ 0x53494c20, 0xffffffe0, "Si3036,8", NULL, mpatch_si3036 }, +{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH }, { 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, { 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, { 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99] @@ -462,12 +463,14 @@ int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * u { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; - unsigned short val; + unsigned short val, bitmask; + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; val = snd_ac97_read_cache(ac97, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1); + ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); if (e->shift_l != e->shift_r) - ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1); + ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1); return 0; } @@ -477,17 +480,19 @@ int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * u ac97_t *ac97 = snd_kcontrol_chip(kcontrol); struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; unsigned short val; - unsigned short mask; + unsigned short mask, bitmask; + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; if (ucontrol->value.enumerated.item[0] > e->mask - 1) return -EINVAL; val = ucontrol->value.enumerated.item[0] << e->shift_l; - mask = (e->mask - 1) << e->shift_l; + mask = (bitmask - 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; + mask |= (bitmask - 1) << e->shift_r; } return snd_ac97_update_bits(ac97, e->reg, mask, val); } @@ -658,14 +663,14 @@ AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1), AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1) }; -static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = { -AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), -AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), -}; - static const snd_kcontrol_new_t snd_ac97_control_eapd = AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1); +static const snd_kcontrol_new_t snd_ac97_controls_modem_switches[2] = { +AC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0), +AC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0) +}; + /* change the existing EAPD control as inverted */ static void set_inv_eapd(ac97_t *ac97, snd_kcontrol_t *kctl) { @@ -1072,9 +1077,9 @@ static void check_volume_resolution(ac97_t *ac97, int reg, unsigned char *lo_max unsigned short val; snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); val = snd_ac97_read(ac97, reg); - if (! *lo_max && (val & cbit[i])) + if (! *lo_max && (val & 0x7f) == cbit[i]) *lo_max = max[i]; - if (! *hi_max && (val & (cbit[i] << 8))) + if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i]) *hi_max = max[i]; if (*lo_max && *hi_max) break; @@ -1526,13 +1531,25 @@ static int snd_ac97_mixer_build(ac97_t * ac97) static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97) { - /* TODO */ + int err, idx; + //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG)); snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff); snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0); snd_ac97_write(ac97, AC97_MISC_AFE, 0x0); + + /* build modem switches */ + for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_modem_switches[idx], ac97))) < 0) + return err; + + /* build chip specific controls */ + if (ac97->build_ops->build_specific) + if ((err = ac97->build_ops->build_specific(ac97)) < 0) + return err; + return 0; } @@ -1872,7 +1889,11 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) goto __access_ok; } - snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ + /* reset to defaults */ + if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO)) + snd_ac97_write(ac97, AC97_RESET, 0); + if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM)) + snd_ac97_write(ac97, AC97_EXTENDED_MID, 0); if (bus->ops->wait) bus->ops->wait(ac97); else { @@ -1964,21 +1985,21 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) /* note: it's important to set the rate at first */ tmp = AC97_MEA_GPIO; if (ac97->ext_mid & AC97_MEI_LINE1) { - snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000); + snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000); tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1; } if (ac97->ext_mid & AC97_MEI_LINE2) { - snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000); + snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000); tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2; } if (ac97->ext_mid & AC97_MEI_HANDSET) { - snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000); + snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000); tmp |= AC97_MEA_HADC | AC97_MEA_HDAC; } - snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); + snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0); udelay(100); /* nothing should be in powerdown mode */ - snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); + snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0); end_time = jiffies + (HZ / 10); do { if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) @@ -2521,11 +2542,11 @@ int snd_ac97_tune_hardware(ac97_t *ac97, struct ac97_quirk *quirk, const char *o return result; } - for (; quirk->vendor; quirk++) { - if (quirk->vendor != ac97->subsystem_vendor) + for (; quirk->subvendor; quirk++) { + if (quirk->subvendor != ac97->subsystem_vendor) continue; - if ((! quirk->mask && quirk->device == ac97->subsystem_device) || - quirk->device == (quirk->mask & ac97->subsystem_device)) { + if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) || + quirk->subdevice == (quirk->mask & ac97->subsystem_device)) { if (quirk->codec_id && quirk->codec_id != ac97->id) continue; snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device); diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 13c34a5..a15eb85 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -64,6 +64,116 @@ static int ac97_update_bits_page(ac97_t *ac97, unsigned short reg, unsigned shor return ret; } +/* + * shared line-in/mic controls + */ +static int ac97_enum_text_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo, + const char **texts, unsigned int nums) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = nums; + if (uinfo->value.enumerated.item > nums - 1) + uinfo->value.enumerated.item = nums - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int ac97_surround_jack_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static const char *texts[] = { "Shared", "Independent" }; + return ac97_enum_text_info(kcontrol, uinfo, texts, 2); +} + +static int ac97_surround_jack_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = ac97->indep_surround; + return 0; +} + +static int ac97_surround_jack_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned char indep = !!ucontrol->value.enumerated.item[0]; + + if (indep != ac97->indep_surround) { + ac97->indep_surround = indep; + if (ac97->build_ops->update_jacks) + ac97->build_ops->update_jacks(ac97); + return 1; + } + return 0; +} + +static int ac97_channel_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static const char *texts[] = { "2ch", "4ch", "6ch" }; + if (kcontrol->private_value) + return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */ + return ac97_enum_text_info(kcontrol, uinfo, texts, 3); +} + +static int ac97_channel_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = ac97->channel_mode; + return 0; +} + +static int ac97_channel_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned char mode = ucontrol->value.enumerated.item[0]; + + if (mode != ac97->channel_mode) { + ac97->channel_mode = mode; + if (ac97->build_ops->update_jacks) + ac97->build_ops->update_jacks(ac97); + return 1; + } + return 0; +} + +#define AC97_SURROUND_JACK_MODE_CTL \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Surround Jack Mode", \ + .info = ac97_surround_jack_mode_info, \ + .get = ac97_surround_jack_mode_get, \ + .put = ac97_surround_jack_mode_put, \ + } +#define AC97_CHANNEL_MODE_CTL \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Channel Mode", \ + .info = ac97_channel_mode_info, \ + .get = ac97_channel_mode_get, \ + .put = ac97_channel_mode_put, \ + } +#define AC97_CHANNEL_MODE_4CH_CTL \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Channel Mode", \ + .info = ac97_channel_mode_info, \ + .get = ac97_channel_mode_get, \ + .put = ac97_channel_mode_put, \ + .private_value = 1, \ + } + +static inline int is_shared_linein(ac97_t *ac97) +{ + return ! ac97->indep_surround && ac97->channel_mode >= 1; +} + +static inline int is_shared_micin(ac97_t *ac97) +{ + return ! ac97->indep_surround && ac97->channel_mode >= 2; +} + + /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ /* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */ @@ -1390,6 +1500,16 @@ static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val); } +static void ad1888_update_jacks(ac97_t *ac97) +{ + /* shared Line-In */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12, + is_shared_linein(ac97) ? 0 : 1 << 12); + /* shared Mic */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11, + is_shared_micin(ac97) ? 0 : 1 << 11); +} + static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1406,8 +1526,8 @@ static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = { .get = snd_ac97_ad1888_downmix_get, .put = snd_ac97_ad1888_downmix_put }, - AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0), - AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0), + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static int patch_ad1888_specific(ac97_t *ac97) @@ -1422,8 +1542,9 @@ static struct snd_ac97_build_ops patch_ad1888_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, .build_specific = patch_ad1888_specific, #ifdef CONFIG_PM - .resume = ad18xx_resume + .resume = ad18xx_resume, #endif + .update_jacks = ad1888_update_jacks, }; int patch_ad1888(ac97_t * ac97) @@ -1459,8 +1580,9 @@ static struct snd_ac97_build_ops patch_ad1980_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, .build_specific = patch_ad1980_specific, #ifdef CONFIG_PM - .resume = ad18xx_resume + .resume = ad18xx_resume, #endif + .update_jacks = ad1888_update_jacks, }; int patch_ad1980(ac97_t * ac97) @@ -1471,10 +1593,21 @@ int patch_ad1980(ac97_t * ac97) } static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = { - AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0), AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0) }; +static void ad1985_update_jacks(ac97_t *ac97) +{ + /* shared Line-In */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12, + is_shared_linein(ac97) ? 0 : 1 << 12); + /* shared Mic */ + snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11, + is_shared_micin(ac97) ? 0 : 1 << 11); + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 1 << 9, + is_shared_micin(ac97) ? 0 : 1 << 9); +} + static int patch_ad1985_specific(ac97_t *ac97) { int err; @@ -1488,8 +1621,9 @@ static struct snd_ac97_build_ops patch_ad1985_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, .build_specific = patch_ad1985_specific, #ifdef CONFIG_PM - .resume = ad18xx_resume + .resume = ad18xx_resume, #endif + .update_jacks = ad1985_update_jacks, }; int patch_ad1985(ac97_t * ac97) @@ -1521,31 +1655,25 @@ int patch_ad1985(ac97_t * ac97) /* * realtek ALC65x/850 codecs */ -static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; - return 0; -} - -static int snd_ac97_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +static void alc650_update_jacks(ac97_t *ac97) { - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - int change, val; - val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10)); - change = (ucontrol->value.integer.value[0] != val); - if (change) { - /* disable/enable vref */ - snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, - ucontrol->value.integer.value[0] ? (1 << 12) : 0); - /* turn on/off center-on-mic */ - snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, - ucontrol->value.integer.value[0] ? (1 << 10) : 0); - /* GPIO0 high for mic */ - snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100, - ucontrol->value.integer.value[0] ? 0 : 0x100); - } - return change; + int shared; + + /* shared Line-In */ + shared = is_shared_linein(ac97); + snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9, + shared ? (1 << 9) : 0); + /* update shared Mic */ + shared = is_shared_micin(ac97); + /* disable/enable vref */ + snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, + shared ? (1 << 12) : 0); + /* turn on/off center-on-mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, + shared ? (1 << 10) : 0); + /* GPIO0 high for mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100, + shared ? 0 : 0x100); } static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { @@ -1558,8 +1686,8 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { /* 6: Independent Master Volume Right */ /* 7: Independent Master Volume Left */ /* 8: reserved */ - AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), - /* 10: mic, see below */ + /* 9: Line-In/Surround share */ + /* 10: Mic/CLFE share */ /* 11-13: in IEC958 controls */ AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0), #if 0 /* always set in patch_alc650 */ @@ -1570,14 +1698,8 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), #endif - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_volsw, - .get = snd_ac97_alc650_mic_get, - .put = snd_ac97_alc650_mic_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { @@ -1601,7 +1723,8 @@ static int patch_alc650_specific(ac97_t * ac97) } static struct snd_ac97_build_ops patch_alc650_ops = { - .build_specific = patch_alc650_specific + .build_specific = patch_alc650_specific, + .update_jacks = alc650_update_jacks }; int patch_alc650(ac97_t * ac97) @@ -1659,37 +1782,27 @@ int patch_alc650(ac97_t * ac97) return 0; } -static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; - return 0; -} - -static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +static void alc655_update_jacks(ac97_t *ac97) { - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - + int shared; + + /* shared Line-In */ + shared = is_shared_linein(ac97); + ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9, + shared ? (1 << 9) : 0, 0); + /* update shared mic */ + shared = is_shared_micin(ac97); /* misc control; vrefout disable */ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, - ucontrol->value.integer.value[0] ? (1 << 12) : 0); - return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10, - ucontrol->value.integer.value[0] ? (1 << 10) : 0, - 0); + shared ? (1 << 12) : 0); + ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10, + shared ? (1 << 10) : 0, 0); } - static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = { AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), - AC97_PAGE_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0, 0), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_volsw, - .get = snd_ac97_alc655_mic_get, - .put = snd_ac97_alc655_mic_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -1759,7 +1872,8 @@ static int patch_alc655_specific(ac97_t * ac97) } static struct snd_ac97_build_ops patch_alc655_ops = { - .build_specific = patch_alc655_specific + .build_specific = patch_alc655_specific, + .update_jacks = alc655_update_jacks }; int patch_alc655(ac97_t * ac97) @@ -1798,63 +1912,33 @@ int patch_alc655(ac97_t * ac97) #define AC97_ALC850_JACK_SELECT 0x76 #define AC97_ALC850_MISC1 0x7a -static int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2; - return 0; -} - -static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +static void alc850_update_jacks(ac97_t *ac97) { - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - + int shared; + + /* shared Line-In */ + shared = is_shared_linein(ac97); /* SURR 1kOhm (bit4), Amp (bit5) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5), - ucontrol->value.integer.value[0] ? (1<<5) : (1<<4)); + shared ? (1<<5) : (1<<4)); /* LINE-IN = 0, SURROUND = 2 */ - return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12, - ucontrol->value.integer.value[0] ? (2<<12) : (0<<12)); -} - -static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2; - return 0; -} - -static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - + snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12, + shared ? (2<<12) : (0<<12)); + /* update shared mic */ + shared = is_shared_micin(ac97); /* Vref disable (bit12), 1kOhm (bit13) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13), - ucontrol->value.integer.value[0] ? (1<<12) : (1<<13)); + shared ? (1<<12) : (1<<13)); /* MIC-IN = 1, CENTER-LFE = 2 */ - return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, - ucontrol->value.integer.value[0] ? (2<<4) : (1<<4)); + snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, + shared ? (2<<4) : (1<<4)); } static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = { AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Line-In As Surround", - .info = snd_ac97_info_volsw, - .get = ac97_alc850_surround_get, - .put = ac97_alc850_surround_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_volsw, - .get = ac97_alc850_mic_get, - .put = ac97_alc850_mic_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, - + AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1), + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static int patch_alc850_specific(ac97_t *ac97) @@ -1871,7 +1955,8 @@ static int patch_alc850_specific(ac97_t *ac97) } static struct snd_ac97_build_ops patch_alc850_ops = { - .build_specific = patch_alc850_specific + .build_specific = patch_alc850_specific, + .update_jacks = alc850_update_jacks }; int patch_alc850(ac97_t *ac97) @@ -1911,9 +1996,17 @@ int patch_alc850(ac97_t *ac97) /* * C-Media CM97xx codecs */ +static void cm9738_update_jacks(ac97_t *ac97) +{ + /* shared Line-In */ + snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10, + is_shared_linein(ac97) ? (1 << 10) : 0); +} + static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = { - AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0), AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0), + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_4CH_CTL, }; static int patch_cm9738_specific(ac97_t * ac97) @@ -1922,7 +2015,8 @@ static int patch_cm9738_specific(ac97_t * ac97) } static struct snd_ac97_build_ops patch_cm9738_ops = { - .build_specific = patch_cm9738_specific + .build_specific = patch_cm9738_specific, + .update_jacks = cm9738_update_jacks }; int patch_cm9738(ac97_t * ac97) @@ -1986,34 +2080,19 @@ static const snd_kcontrol_new_t snd_ac97_cm9739_controls_spdif[] = { /* BIT 8: SPD32 - 32bit SPDIF - not supported yet */ }; -static int snd_ac97_cm9739_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000) - ucontrol->value.integer.value[0] = 1; - else - ucontrol->value.integer.value[0] = 0; - return 0; -} - -static int snd_ac97_cm9739_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static void cm9739_update_jacks(ac97_t *ac97) { - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000, - ucontrol->value.integer.value[0] ? - 0x1000 : 0x2000); + /* shared Line-In */ + snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10, + is_shared_linein(ac97) ? (1 << 10) : 0); + /* shared Mic */ + snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000, + is_shared_micin(ac97) ? 0x1000 : 0x2000); } static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = { - AC97_SINGLE("Line-In As Surround", AC97_CM9739_MULTI_CHAN, 10, 1, 0), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_volsw, - .get = snd_ac97_cm9739_center_mic_get, - .put = snd_ac97_cm9739_center_mic_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static int patch_cm9739_specific(ac97_t * ac97) @@ -2028,7 +2107,8 @@ static int patch_cm9739_post_spdif(ac97_t * ac97) static struct snd_ac97_build_ops patch_cm9739_ops = { .build_specific = patch_cm9739_specific, - .build_post_spdif = patch_cm9739_post_spdif + .build_post_spdif = patch_cm9739_post_spdif, + .update_jacks = cm9739_update_jacks }; int patch_cm9739(ac97_t * ac97) @@ -2087,71 +2167,97 @@ int patch_cm9739(ac97_t * ac97) } #define AC97_CM9761_MULTI_CHAN 0x64 +#define AC97_CM9761_FUNC 0x66 #define AC97_CM9761_SPDIF_CTRL 0x6c -static int snd_ac97_cm9761_linein_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x0400) - ucontrol->value.integer.value[0] = 1; - else - ucontrol->value.integer.value[0] = 0; - return 0; -} - -static int snd_ac97_cm9761_linein_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static void cm9761_update_jacks(ac97_t *ac97) { - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - unsigned short vals[2][2] = { + unsigned short surr_vals[2][2] = { { 0x0008, 0x0400 }, /* off, on */ { 0x0000, 0x0408 }, /* off, on (9761-82 rev.B) */ }; - return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x0408, - vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]); + unsigned short clfe_vals[2][2] = { + { 0x2000, 0x1880 }, /* off, on */ + { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */ + }; + + /* shared Line-In */ + snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x0408, + surr_vals[ac97->spec.dev_flags][is_shared_linein(ac97)]); + /* shared Mic */ + snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3880, + clfe_vals[ac97->spec.dev_flags][is_shared_micin(ac97)]); +} + +static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = { + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, +}; + +static int cm9761_spdif_out_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; } -static int snd_ac97_cm9761_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int cm9761_spdif_out_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000) - ucontrol->value.integer.value[0] = 1; + + if (ac97->regs[AC97_CM9761_FUNC] & 0x1) + ucontrol->value.enumerated.item[0] = 2; /* SPDIF-loopback */ + else if (ac97->regs[AC97_CM9761_SPDIF_CTRL] & 0x2) + ucontrol->value.enumerated.item[0] = 1; /* ADC loopback */ else - ucontrol->value.integer.value[0] = 0; - if (ac97->spec.dev_flags) /* 9761-82 rev.B */ - ucontrol->value.integer.value[0] = !ucontrol->value.integer.value[0]; + ucontrol->value.enumerated.item[0] = 0; /* AC-link */ return 0; } -static int snd_ac97_cm9761_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int cm9761_spdif_out_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - unsigned short vals[2][2] = { - { 0x2000, 0x1880 }, /* off, on */ - { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */ - }; - return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3880, - vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]); + + if (ucontrol->value.enumerated.item[0] == 2) + return snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0x1); + snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0); + return snd_ac97_update_bits(ac97, AC97_CM9761_SPDIF_CTRL, 0x2, + ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0); } -static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Line-In As Surround", - .info = snd_ac97_info_volsw, - .get = snd_ac97_cm9761_linein_rear_get, - .put = snd_ac97_cm9761_linein_rear_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_volsw, - .get = snd_ac97_cm9761_center_mic_get, - .put = snd_ac97_cm9761_center_mic_put, - .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ +static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" }; +static const struct ac97_enum cm9761_dac_clock_enum = + AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock); + +static const snd_kcontrol_new_t snd_ac97_cm9761_controls_spdif[] = { + { /* BIT 1: SPDIFS */ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + .info = cm9761_spdif_out_source_info, + .get = cm9761_spdif_out_source_get, + .put = cm9761_spdif_out_source_put, }, + /* BIT 2: IG_SPIV */ + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9761_SPDIF_CTRL, 2, 1, 0), + /* BIT 3: SPI2F */ + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9761_SPDIF_CTRL, 3, 1, 0), + /* BIT 4: SPI2SDI */ + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9761_SPDIF_CTRL, 4, 1, 0), + /* BIT 9-10: DAC_CTL */ + AC97_ENUM("DAC Clock Source", cm9761_dac_clock_enum), }; +static int patch_cm9761_post_spdif(ac97_t * ac97) +{ + return patch_build_controls(ac97, snd_ac97_cm9761_controls_spdif, ARRAY_SIZE(snd_ac97_cm9761_controls_spdif)); +} + static int patch_cm9761_specific(ac97_t * ac97) { return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls)); @@ -2159,7 +2265,8 @@ static int patch_cm9761_specific(ac97_t * ac97) static struct snd_ac97_build_ops patch_cm9761_ops = { .build_specific = patch_cm9761_specific, - .build_post_spdif = patch_cm9739_post_spdif /* hope it's identical... */ + .build_post_spdif = patch_cm9761_post_spdif, + .update_jacks = cm9761_update_jacks }; int patch_cm9761(ac97_t *ac97) @@ -2193,24 +2300,25 @@ int patch_cm9761(ac97_t *ac97) /* to be sure: we overwrite the ext status bits */ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0); /* Don't set 0x0200 here. This results in the silent analog output */ - snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0009); + snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0001); /* enable spdif-in */ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ /* set-up multi channel */ /* bit 15: pc master beep off - * bit 14: ?? + * bit 14: pin47 = EAPD/SPDIF * bit 13: vref ctl [= cm9739] - * bit 12: center/mic [= cm9739] (reverted on rev B) - * bit 11: ?? (mic/center/lfe) (reverted on rev B) - * bit 10: suddound/line [= cm9739] - * bit 9: mix 2 surround - * bit 8: ? - * bit 7: ?? (mic/center/lfe) - * bit 4: ?? (front) - * bit 3: ?? (line-in/rear share) (revereted with rev B) - * bit 2: ?? (surround) - * bit 1: front mic - * bit 0: mic boost + * bit 12: CLFE control (reverted on rev B) + * bit 11: Mic/center share (reverted on rev B) + * bit 10: suddound/line share + * bit 9: Analog-in mix -> surround + * bit 8: Analog-in mix -> CLFE + * bit 7: Mic/LFE share (mic/center/lfe) + * bit 5: vref select (9761A) + * bit 4: front control + * bit 3: surround control (revereted with rev B) + * bit 2: front mic + * bit 1: stereo mic + * bit 0: mic boost level (0=20dB, 1=30dB) */ #if 0 @@ -2230,6 +2338,47 @@ int patch_cm9761(ac97_t *ac97) return 0; } +#define AC97_CM9780_SIDE 0x60 +#define AC97_CM9780_JACK 0x62 +#define AC97_CM9780_MIXER 0x64 +#define AC97_CM9780_MULTI_CHAN 0x66 +#define AC97_CM9780_SPDIF 0x6c + +static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" }; +static const struct ac97_enum cm9780_ch_select_enum = + AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select); +static const snd_kcontrol_new_t cm9780_controls[] = { + AC97_DOUBLE("Side Playback Switch", AC97_CM9780_SIDE, 15, 7, 1, 1), + AC97_DOUBLE("Side Playback Volume", AC97_CM9780_SIDE, 8, 0, 31, 0), + AC97_ENUM("Side Playback Route", cm9780_ch_select_enum), +}; + +static int patch_cm9780_specific(ac97_t *ac97) +{ + return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls)); +} + +static struct snd_ac97_build_ops patch_cm9780_ops = { + .build_specific = patch_cm9780_specific, + .build_post_spdif = patch_cm9761_post_spdif /* identical with CM9761 */ +}; + +int patch_cm9780(ac97_t *ac97) +{ + unsigned short val; + + ac97->build_ops = &patch_cm9780_ops; + + /* enable spdif */ + if (ac97->ext_id & AC97_EI_SPDIF) { + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ + val = snd_ac97_read(ac97, AC97_CM9780_SPDIF); + val |= 0x1; /* SPDI_EN */ + snd_ac97_write_cache(ac97, AC97_CM9780_SPDIF, val); + } + + return 0; +} /* * VIA VT1616 codec @@ -2263,9 +2412,21 @@ int patch_vt1616(ac97_t * ac97) return 0; } +/* + */ +static void it2646_update_jacks(ac97_t *ac97) +{ + /* shared Line-In */ + snd_ac97_update_bits(ac97, 0x76, 1 << 9, + is_shared_linein(ac97) ? (1<<9) : 0); + /* shared Mic */ + snd_ac97_update_bits(ac97, 0x76, 1 << 10, + is_shared_micin(ac97) ? (1<<10) : 0); +} + static const snd_kcontrol_new_t snd_ac97_controls_it2646[] = { - AC97_SINGLE("Line-In As Surround", 0x76, 9, 1, 0), - AC97_SINGLE("Mic As Center/LFE", 0x76, 10, 1, 0), + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_it2646[] = { @@ -2285,7 +2446,8 @@ static int patch_it2646_specific(ac97_t * ac97) } static struct snd_ac97_build_ops patch_it2646_ops = { - .build_specific = patch_it2646_specific + .build_specific = patch_it2646_specific, + .update_jacks = it2646_update_jacks }; int patch_it2646(ac97_t * ac97) @@ -2297,12 +2459,29 @@ int patch_it2646(ac97_t * ac97) return 0; } -/* Si3036/8 specific registers */ +/* + * Si3036 codec + */ + #define AC97_SI3036_CHIP_ID 0x5a +#define AC97_SI3036_LINE_CFG 0x5c + +static const snd_kcontrol_new_t snd_ac97_controls_si3036[] = { +AC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1) +}; + +static int patch_si3036_specific(ac97_t * ac97) +{ + return patch_build_controls(ac97, snd_ac97_controls_si3036, ARRAY_SIZE(snd_ac97_controls_si3036)); +} + +static struct snd_ac97_build_ops patch_si3036_ops = { + .build_specific = patch_si3036_specific, +}; int mpatch_si3036(ac97_t * ac97) { - //printk("mpatch_si3036: chip id = %x\n", snd_ac97_read(ac97, 0x5a)); + ac97->build_ops = &patch_si3036_ops; snd_ac97_write_cache(ac97, 0x5c, 0xf210 ); snd_ac97_write_cache(ac97, 0x68, 0); return 0; diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 6db51c9..7b7377d 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -54,6 +54,7 @@ int patch_alc850(ac97_t * ac97); int patch_cm9738(ac97_t * ac97); int patch_cm9739(ac97_t * ac97); int patch_cm9761(ac97_t * ac97); +int patch_cm9780(ac97_t * ac97); int patch_vt1616(ac97_t * ac97); int patch_it2646(ac97_t * ac97); int mpatch_si3036(ac97_t * ac97); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 984d5d4..eb5c36d 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -98,6 +98,8 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); #define ALI_LEF_CHANNEL 23 #define ALI_SURR_LEFT_CHANNEL 26 #define ALI_SURR_RIGHT_CHANNEL 25 +#define ALI_MODEM_IN_CHANNEL 21 +#define ALI_MODEM_OUT_CHANNEL 20 #define SNDRV_ALI_VOICE_TYPE_PCM 01 #define SNDRV_ALI_VOICE_TYPE_OTH 02 @@ -122,7 +124,15 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); #define ALI_SCTRL 0x48 #define ALI_SPDIF_OUT_ENABLE 0x20 +#define ALI_SCTRL_LINE_IN2 (1 << 9) +#define ALI_SCTRL_GPIO_IN2 (1 << 13) +#define ALI_SCTRL_LINE_OUT_EN (1 << 20) +#define ALI_SCTRL_GPIO_OUT_EN (1 << 23) +#define ALI_SCTRL_CODEC1_READY (1 << 24) +#define ALI_SCTRL_CODEC2_READY (1 << 25) #define ALI_AC97_GPIO 0x4c +#define ALI_AC97_GPIO_ENABLE 0x8000 +#define ALI_AC97_GPIO_DATA_SHIFT 16 #define ALI_SPDIF_CS 0x70 #define ALI_SPDIF_CTRL 0x74 #define ALI_SPDIF_IN_FUNC_ENABLE 0x02 @@ -143,6 +153,7 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); #define TARGET_REACHED 0x00008000 #define MIXER_OVERFLOW 0x00000800 #define MIXER_UNDERFLOW 0x00000400 + #define GPIO_IRQ 0x01000000 #define ALI_SBBL_SBCL 0xc0 #define ALI_SBCTRL_SBE2R_SBDD 0xc4 #define ALI_STIMER 0xc8 @@ -162,6 +173,9 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); #define ALI_REG(codec, x) ((codec)->port + x) +#define MAX_CODECS 2 + + typedef struct snd_stru_ali ali_t; typedef struct snd_ali_stru_voice snd_ali_voice_t; @@ -245,7 +259,7 @@ struct snd_stru_ali { struct pci_dev *pci_m7101; snd_card_t *card; - snd_pcm_t *pcm; + snd_pcm_t *pcm[MAX_CODECS]; alidev_t synth; snd_ali_channel_control_t chregs; @@ -255,8 +269,10 @@ struct snd_stru_ali { unsigned int spurious_irq_count; unsigned int spurious_irq_max_delta; + unsigned int num_of_codecs; + ac97_bus_t *ac97_bus; - ac97_t *ac97; + ac97_t *ac97[MAX_CODECS]; unsigned short ac97_ext_id; unsigned short ac97_ext_status; @@ -489,7 +505,12 @@ static void snd_ali_codec_write(ac97_t *ac97, ali_t *codec = ac97->private_data; snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); - snd_ali_codec_poke(codec, 0, reg, val); + if(reg == AC97_GPIO_STATUS) { + outl((val << ALI_AC97_GPIO_DATA_SHIFT)|ALI_AC97_GPIO_ENABLE, + ALI_REG(codec, ALI_AC97_GPIO)); + return; + } + snd_ali_codec_poke(codec, ac97->num, reg, val); return ; } @@ -499,7 +520,7 @@ static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg) ali_t *codec = ac97->private_data; snd_ali_printk("codec_read reg=%xh.\n", reg); - return (snd_ali_codec_peek(codec, 0, reg)); + return (snd_ali_codec_peek(codec, ac97->num, reg)); } /* @@ -1051,7 +1072,7 @@ static irqreturn_t snd_ali_card_interrupt(int irq, } -static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec) +static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec, int channel) { snd_ali_voice_t *pvoice = NULL; unsigned long flags; @@ -1061,7 +1082,8 @@ static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec) spin_lock_irqsave(&codec->voice_alloc, flags); if (type == SNDRV_ALI_VOICE_TYPE_PCM) { - idx = snd_ali_find_free_channel(codec,rec); + idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) : + snd_ali_find_free_channel(codec,rec); if(idx < 0) { snd_printk("ali_alloc_voice: err.\n"); spin_unlock_irqrestore(&codec->voice_alloc, flags); @@ -1297,7 +1319,7 @@ static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream, if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) { if (evoice == NULL) { - evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0, -1); if (evoice == NULL) return -ENOMEM; pvoice->extra = evoice; @@ -1328,13 +1350,13 @@ static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream) return 0; } -static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream, +static int snd_ali_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } -static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream) +static int snd_ali_hw_free(snd_pcm_substream_t * substream) { return snd_pcm_lib_free_pages(substream); } @@ -1428,7 +1450,7 @@ static int snd_ali_playback_prepare(snd_pcm_substream_t * substream) } -static int snd_ali_capture_prepare(snd_pcm_substream_t * substream) +static int snd_ali_prepare(snd_pcm_substream_t * substream) { ali_t *codec = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; @@ -1446,11 +1468,13 @@ static int snd_ali_capture_prepare(snd_pcm_substream_t * substream) spin_lock_irqsave(&codec->reg_lock, flags); - snd_ali_printk("capture_prepare...\n"); + snd_ali_printk("ali_prepare...\n"); snd_ali_enable_special_channel(codec,pvoice->number); - Delta = snd_ali_convert_rate(runtime->rate, 1); + Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL || + pvoice->number == ALI_MODEM_OUT_CHANNEL) ? + 0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode); // Prepare capture intr channel if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { @@ -1534,7 +1558,7 @@ static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream } -static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream) +static snd_pcm_uframes_t snd_ali_pointer(snd_pcm_substream_t *substream) { ali_t *codec = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; @@ -1616,7 +1640,8 @@ static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime) } } -static int snd_ali_playback_open(snd_pcm_substream_t * substream) +static int snd_ali_open(snd_pcm_substream_t * substream, int rec, int channel, + snd_pcm_hardware_t *phw) { ali_t *codec = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; @@ -1624,7 +1649,7 @@ static int snd_ali_playback_open(snd_pcm_substream_t * substream) unsigned long flags = 0; spin_lock_irqsave(&codec->reg_lock, flags); - pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec, channel); if (pvoice == NULL) { spin_unlock_irqrestore(&codec->reg_lock, flags); return -EAGAIN; @@ -1636,49 +1661,31 @@ static int snd_ali_playback_open(snd_pcm_substream_t * substream) runtime->private_data = pvoice; runtime->private_free = snd_ali_pcm_free_substream; - runtime->hw = snd_ali_playback; + runtime->hw = *phw; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); return 0; } +static int snd_ali_playback_open(snd_pcm_substream_t * substream) +{ + return snd_ali_open(substream, 0, -1, &snd_ali_playback); +} static int snd_ali_capture_open(snd_pcm_substream_t * substream) { - ali_t *codec = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_ali_voice_t *pvoice; - unsigned long flags; - - spin_lock_irqsave(&codec->reg_lock, flags); - pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1); - if (pvoice == NULL) { - spin_unlock_irqrestore(&codec->reg_lock, flags); - return -EAGAIN; - } - pvoice->codec = codec; - spin_unlock_irqrestore(&codec->reg_lock, flags); - - pvoice->substream = substream; - runtime->private_data = pvoice; - runtime->private_free = snd_ali_pcm_free_substream; - runtime->hw = snd_ali_capture; - snd_pcm_set_sync(substream); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); - return 0; + return snd_ali_open(substream, 1, -1, &snd_ali_capture); } - static int snd_ali_playback_close(snd_pcm_substream_t * substream) { return 0; } -static int snd_ali_capture_close(snd_pcm_substream_t * substream) +static int snd_ali_close(snd_pcm_substream_t * substream) { ali_t *codec = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) substream->runtime->private_data; snd_ali_disable_special_channel(codec,pvoice->number); @@ -1698,29 +1705,121 @@ static snd_pcm_ops_t snd_ali_playback_ops = { static snd_pcm_ops_t snd_ali_capture_ops = { .open = snd_ali_capture_open, - .close = snd_ali_capture_close, + .close = snd_ali_close, .ioctl = snd_ali_ioctl, - .hw_params = snd_ali_capture_hw_params, - .hw_free = snd_ali_capture_hw_free, - .prepare = snd_ali_capture_prepare, + .hw_params = snd_ali_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_pointer, +}; + +/* + * Modem PCM + */ + +static int snd_ali_modem_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ali_t *chip = snd_pcm_substream_chip(substream); + unsigned int modem_num = chip->num_of_codecs - 1; + snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE, params_rate(hw_params)); + snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0); + return snd_ali_hw_params(substream, hw_params); +} + +static snd_pcm_hardware_t snd_ali_modem = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT|SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ali_modem_open(snd_pcm_substream_t * substream, int rec, int channel) +{ + static unsigned int rates [] = {8000,9600,12000,16000}; + static snd_pcm_hw_constraint_list_t hw_constraint_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + int err = snd_ali_open(substream, rec, channel, &snd_ali_modem); + if (err) + return err; + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); +} + +static int snd_ali_modem_playback_open(snd_pcm_substream_t * substream) +{ + return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL); +} + +static int snd_ali_modem_capture_open(snd_pcm_substream_t * substream) +{ + return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL); +} + +static snd_pcm_ops_t snd_ali_modem_playback_ops = { + .open = snd_ali_modem_playback_open, + .close = snd_ali_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_modem_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_pointer, +}; + +static snd_pcm_ops_t snd_ali_modem_capture_ops = { + .open = snd_ali_modem_capture_open, + .close = snd_ali_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_modem_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, .trigger = snd_ali_trigger, - .pointer = snd_ali_capture_pointer, + .pointer = snd_ali_pointer, +}; + + +struct ali_pcm_description { + char *name; + unsigned int playback_num; + unsigned int capture_num; + snd_pcm_ops_t *playback_ops; + snd_pcm_ops_t *capture_ops; }; static void snd_ali_pcm_free(snd_pcm_t *pcm) { ali_t *codec = pcm->private_data; - codec->pcm = NULL; + codec->pcm[pcm->device] = NULL; } -static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm) + +static int __devinit snd_ali_pcm(ali_t * codec, int device, struct ali_pcm_description *desc) { snd_pcm_t *pcm; int err; - if (rpcm) *rpcm = NULL; - err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm); + err = snd_pcm_new(codec->card, desc->name, device, + desc->playback_num, desc->capture_num, &pcm); if (err < 0) { snd_printk("snd_ali_pcm: err called snd_pcm_new.\n"); return err; @@ -1728,20 +1827,36 @@ static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm) pcm->private_data = codec; pcm->private_free = snd_ali_pcm_free; pcm->info_flags = 0; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); + if (desc->playback_ops) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, desc->playback_ops); + if (desc->capture_ops) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, desc->capture_ops); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(codec->pci), 64*1024, 128*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "ALI 5451"); - codec->pcm = pcm; - if (rpcm) *rpcm = pcm; + strcpy(pcm->name, desc->name); + codec->pcm[0] = pcm; return 0; } +struct ali_pcm_description ali_pcms[] = { + { "ALI 5451", ALI_CHANNELS, 1, &snd_ali_playback_ops, &snd_ali_capture_ops }, + { "ALI 5451 modem", 1, 1, &snd_ali_modem_playback_ops, &snd_ali_modem_capture_ops } +}; + +static int __devinit snd_ali_build_pcms(ali_t *codec) +{ + int i, err; + for(i = 0 ; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms) ; i++) + if((err = snd_ali_pcm(codec, i, &ali_pcms[i])) < 0) + return err; + return 0; +} + + #define ALI5451_SPDIF(xname, xindex, value) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ .info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ @@ -1860,14 +1975,14 @@ static void snd_ali_mixer_free_ac97_bus(ac97_bus_t *bus) static void snd_ali_mixer_free_ac97(ac97_t *ac97) { ali_t *codec = ac97->private_data; - codec->ac97 = NULL; + codec->ac97[ac97->num] = NULL; } static int __devinit snd_ali_mixer(ali_t * codec) { ac97_template_t ac97; unsigned int idx; - int err; + int i, err; static ac97_bus_ops_t ops = { .write = snd_ali_codec_write, .read = snd_ali_codec_read, @@ -1880,10 +1995,16 @@ static int __devinit snd_ali_mixer(ali_t * codec) memset(&ac97, 0, sizeof(ac97)); ac97.private_data = codec; ac97.private_free = snd_ali_mixer_free_ac97; - if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97)) < 0) { - snd_printk("ali mixer creating error.\n"); + + for ( i = 0 ; i < codec->num_of_codecs ; i++) { + ac97.num = i; + if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i])) < 0) { + snd_printk("ali mixer %d creating error.\n", i); + if(i == 0) return err; } + } + if (codec->spdif_support) { for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) { err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); @@ -1904,8 +2025,12 @@ static int ali_suspend(snd_card_t *card, pm_message_t state) if (! im) return 0; - snd_pcm_suspend_all(chip->pcm); - snd_ac97_suspend(chip->ac97); + for(i = 0 ; i < chip->num_of_codecs ; i++) { + if (chip->pcm[i]) + snd_pcm_suspend_all(chip->pcm[i]); + if(chip->ac97[i]) + snd_ac97_suspend(chip->ac97[i]); + } spin_lock_irq(&chip->reg_lock); @@ -1969,7 +2094,9 @@ static int ali_resume(snd_card_t *card) spin_unlock_irq(&chip->reg_lock); - snd_ac97_resume(chip->ac97); + for(i = 0 ; i < chip->num_of_codecs ; i++) + if(chip->ac97[i]) + snd_ac97_resume(chip->ac97[i]); return 0; } @@ -2036,11 +2163,37 @@ static int snd_ali_chip_init(ali_t *codec) codec->spdif_mask = 0x00000002; } + codec->num_of_codecs = 1; + + /* secondary codec - modem */ + if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) { + codec->num_of_codecs++; + outl(inl(ALI_REG(codec, ALI_SCTRL)) | + (ALI_SCTRL_LINE_IN2|ALI_SCTRL_GPIO_IN2|ALI_SCTRL_LINE_OUT_EN), + ALI_REG(codec, ALI_SCTRL)); + } + snd_ali_printk("chip initialize succeed.\n"); return 0; } +/* proc for register dump */ +static void snd_ali_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buf) +{ + ali_t *codec = entry->private_data; + int i; + for(i = 0 ; i < 256 ; i+= 4) + snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i))); +} + +static void __devinit snd_ali_proc_init(ali_t *codec) +{ + snd_info_entry_t *entry; + if(!snd_card_proc_new(codec->card, "ali5451", &entry)) + snd_info_set_text_ops(entry, codec, 1024, snd_ali_proc_read); +} + static int __devinit snd_ali_resources(ali_t *codec) { int err; @@ -2233,11 +2386,13 @@ static int __devinit snd_ali_probe(struct pci_dev *pci, } snd_ali_printk("pcm building ...\n"); - if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) { + if ((err = snd_ali_build_pcms(codec)) < 0) { snd_card_free(card); return err; } + snd_ali_proc_init(codec); + strcpy(card->driver, "ALI5451"); strcpy(card->shortname, "ALI 5451"); @@ -2270,7 +2425,7 @@ static struct pci_driver driver = { static int __init alsa_card_ali_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_ali_exit(void) diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index f1a5f57..ca28b22 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -367,7 +367,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs * if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */ snd_pcm_period_elapsed(chip->capture_substream); if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */ - snd_mpu401_uart_interrupt(irq, chip->rmidi, regs); + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); /* release the gcr */ outb(gcr_status, chip->alt_port + 0xe); @@ -777,7 +777,7 @@ static struct pci_driver driver = { static int __init alsa_card_als4000_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_als4000_exit(void) diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 6b04c0a..cafab4a 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1334,8 +1334,8 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *r static struct ac97_quirk ac97_quirks[] __devinitdata = { { - .vendor = 0x103c, - .device = 0x006b, + .subvendor = 0x103c, + .subdevice = 0x006b, .name = "HP Pavilion ZV5030US", .type = AC97_TUNE_MUTE_LED }, @@ -1645,7 +1645,7 @@ static struct pci_driver driver = { static int __init alsa_card_atiixp_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_atiixp_exit(void) diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index fb7cece..a6b4b8d 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -463,6 +463,11 @@ static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg) static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { atiixp_t *chip = ac97->private_data; + if (reg == AC97_GPIO_STATUS) { + atiixp_write(chip, MODEM_OUT_GPIO, + (val << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN); + return; + } snd_atiixp_codec_write(chip, ac97->num, reg, val); } @@ -663,44 +668,33 @@ static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { atiixp_t *chip = snd_pcm_substream_chip(substream); atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data; - unsigned int reg = 0; - int i; + int err = 0; snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL); - if (cmd != SNDRV_PCM_TRIGGER_START && cmd != SNDRV_PCM_TRIGGER_STOP) - return -EINVAL; - spin_lock(&chip->reg_lock); - - /* hook off/on: via GPIO_OUT */ - for (i = 0; i < NUM_ATI_CODECS; i++) { - if (chip->ac97[i]) { - reg = snd_ac97_read(chip->ac97[i], AC97_GPIO_STATUS); - break; - } - } - if(cmd == SNDRV_PCM_TRIGGER_START) - reg |= AC97_GPIO_LINE1_OH; - else - reg &= ~AC97_GPIO_LINE1_OH; - reg = (reg << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN ; - atiixp_write(chip, MODEM_OUT_GPIO, reg); - - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch(cmd) { + case SNDRV_PCM_TRIGGER_START: dma->ops->enable_transfer(chip, 1); dma->running = 1; - } else { + break; + case SNDRV_PCM_TRIGGER_STOP: dma->ops->enable_transfer(chip, 0); dma->running = 0; + break; + default: + err = -EINVAL; + break; } + if (! err) { snd_atiixp_check_bus_busy(chip); if (cmd == SNDRV_PCM_TRIGGER_STOP) { dma->ops->flush_dma(chip); snd_atiixp_check_bus_busy(chip); } + } spin_unlock(&chip->reg_lock); - return 0; + return err; } @@ -1332,7 +1326,7 @@ static struct pci_driver driver = { static int __init alsa_card_atiixp_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_atiixp_exit(void) diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 889b4a1..f6236c6 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -375,7 +375,7 @@ static struct pci_driver driver = { // initialization of the module static int __init alsa_card_vortex_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } // clean up the module diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index b8ae534..72bba7b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1520,7 +1520,7 @@ static int __init alsa_card_azf3328_init(void) { int err; snd_azf3328_dbgcallenter(); - err = pci_module_init(&driver); + err = pci_register_driver(&driver); snd_azf3328_dbgcallleave(); return err; } diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 89a7ffe..defdc5a 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -918,7 +918,7 @@ static int __init alsa_card_bt87x_init(void) { if (load_all) driver.id_table = snd_bt87x_default_ids; - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_bt87x_exit(void) diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index deb0288..da09cab 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.20 + * Version: 0.0.21 * * FEATURES currently supported: * See ca0106_main.c for features. @@ -45,6 +45,8 @@ * Added I2C and SPI registers. Filled in interrupt enable. * 0.0.20 * Added GPIO info for SB Live 24bit. + * 0.0.21 + * Implement support for Line-in capture on SB Live 24bit. * * * This code was initally based on code from ALSA's emu10k1x.c which is: @@ -152,7 +154,7 @@ * bit 9 0 = Mute / 1 = Analog out. * bit 10 0 = Line-in / 1 = Mic-in. * bit 11 0 = ? / 1 = ? - * bit 12 0 = ? / 1 = ? + * bit 12 0 = 48 Khz / 1 = 96 Khz Analog out on SB Live 24bit. * bit 13 0 = ? / 1 = ? * bit 14 0 = Mute / 1 = Analog out * bit 15 0 = ? / 1 = ? @@ -475,9 +477,56 @@ /* Causes interrupts based on timer intervals. */ #define SPI 0x7a /* SPI: Serial Interface Register */ #define I2C_A 0x7b /* I2C Address. 32 bit */ -#define I2C_0 0x7c /* I2C Data Port 0. 32 bit */ -#define I2C_1 0x7d /* I2C Data Port 1. 32 bit */ +#define I2C_D0 0x7c /* I2C Data Port 0. 32 bit */ +#define I2C_D1 0x7d /* I2C Data Port 1. 32 bit */ +//I2C values +#define I2C_A_ADC_ADD_MASK 0x000000fe //The address is a 7 bit address +#define I2C_A_ADC_RW_MASK 0x00000001 //bit mask for R/W +#define I2C_A_ADC_TRANS_MASK 0x00000010 //Bit mask for I2c address DAC value +#define I2C_A_ADC_ABORT_MASK 0x00000020 //Bit mask for I2C transaction abort flag +#define I2C_A_ADC_LAST_MASK 0x00000040 //Bit mask for Last word transaction +#define I2C_A_ADC_BYTE_MASK 0x00000080 //Bit mask for Byte Mode +#define I2C_A_ADC_ADD 0x00000034 //This is the Device address for ADC +#define I2C_A_ADC_READ 0x00000001 //To perform a read operation +#define I2C_A_ADC_START 0x00000100 //Start I2C transaction +#define I2C_A_ADC_ABORT 0x00000200 //I2C transaction abort +#define I2C_A_ADC_LAST 0x00000400 //I2C last transaction +#define I2C_A_ADC_BYTE 0x00000800 //I2C one byte mode + +#define I2C_D_ADC_REG_MASK 0xfe000000 //ADC address register +#define I2C_D_ADC_DAT_MASK 0x01ff0000 //ADC data register + +#define ADC_TIMEOUT 0x00000007 //ADC Timeout Clock Disable +#define ADC_IFC_CTRL 0x0000000b //ADC Interface Control +#define ADC_MASTER 0x0000000c //ADC Master Mode Control +#define ADC_POWER 0x0000000d //ADC PowerDown Control +#define ADC_ATTEN_ADCL 0x0000000e //ADC Attenuation ADCL +#define ADC_ATTEN_ADCR 0x0000000f //ADC Attenuation ADCR +#define ADC_ALC_CTRL1 0x00000010 //ADC ALC Control 1 +#define ADC_ALC_CTRL2 0x00000011 //ADC ALC Control 2 +#define ADC_ALC_CTRL3 0x00000012 //ADC ALC Control 3 +#define ADC_NOISE_CTRL 0x00000013 //ADC Noise Gate Control +#define ADC_LIMIT_CTRL 0x00000014 //ADC Limiter Control +#define ADC_MUX 0x00000015 //ADC Mux offset + +#if 0 +/* FIXME: Not tested yet. */ +#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain +#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB +#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute +#define ADC_MUTE 0x000000c0 //Value to mute ADC +#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select +#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock +#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter +#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window +#endif + +#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux +#define ADC_MUX_MIC 0x00000002 //Value to select Mic at ADC Mux +#define ADC_MUX_LINEIN 0x00000004 //Value to select LineIn at ADC Mux +#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) +#define ADC_MUX_AUX 0x00000008 //Value to select Aux at ADC Mux #define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */ #define PCM_FRONT_CHANNEL 0 @@ -508,9 +557,18 @@ struct snd_ca0106_pcm { unsigned short running; }; +typedef struct { + u32 serial; + char * name; + int ac97; + int gpio_type; + int i2c_adc; +} ca0106_details_t; + // definition of the chip-specific record struct snd_ca0106 { snd_card_t *card; + ca0106_details_t *details; struct pci_dev *pci; unsigned long port; @@ -531,6 +589,7 @@ struct snd_ca0106 { u32 spdif_bits[4]; /* s/pdif out setup */ int spdif_enable; int capture_source; + int capture_mic_line_in; struct snd_dma_buffer buffer; }; @@ -547,3 +606,6 @@ void snd_ca0106_ptr_write(ca0106_t *emu, unsigned int chn, unsigned int data); +int snd_ca0106_i2c_write(ca0106_t *emu, u32 reg, u32 value); + + diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 82533b4..95c2892 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.22 + * Version: 0.0.23 * * FEATURES currently supported: * Front, Rear and Center/LFE. @@ -77,6 +77,8 @@ * Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.) * 0.0.22 * Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901 + * 0.0.23 + * Implement support for Line-in capture on SB Live 24bit. * * BUGS: * Some stability problems when unloading the snd-ca0106 kernel module. @@ -136,6 +138,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/moduleparam.h> +#include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> @@ -161,18 +164,32 @@ MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard."); #include "ca0106.h" -typedef struct { - u32 serial; - char * name; -} ca0106_names_t; - -static ca0106_names_t ca0106_chip_names[] = { - { 0x10021102, "AudigyLS [SB0310]"} , - { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */ - { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */ - { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ - { 0x10091462, "MSI K8N Diamond MB [SB0438]"}, /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ - { 0, "AudigyLS [Unknown]" } +static ca0106_details_t ca0106_chip_details[] = { + /* AudigyLS[SB0310] */ + { .serial = 0x10021102, + .name = "AudigyLS [SB0310]", + .ac97 = 1 } , + /* Unknown AudigyLS that also says SB0310 on it */ + { .serial = 0x10051102, + .name = "AudigyLS [SB0310b]", + .ac97 = 1 } , + /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */ + { .serial = 0x10061102, + .name = "Live! 7.1 24bit [SB0410]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ + { .serial = 0x10071102, + .name = "Live! 7.1 24bit [SB0413]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ + { .serial = 0x10091462, + .name = "MSI K8N Diamond MB [SB0438]", + .gpio_type = 1, + .i2c_adc = 1 } , + { .serial = 0, + .name = "AudigyLS [Unknown]" } }; /* hardware definition */ @@ -200,10 +217,10 @@ static snd_pcm_hardware_t snd_ca0106_capture_hw = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .rate_min = 44100, + .rate_max = 192000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = ((65536 - 64) * 8), @@ -246,6 +263,62 @@ void snd_ca0106_ptr_write(ca0106_t *emu, spin_unlock_irqrestore(&emu->emu_lock, flags); } +int snd_ca0106_i2c_write(ca0106_t *emu, + u32 reg, + u32 value) +{ + u32 tmp; + int timeout=0; + int status; + int retry; + if ((reg > 0x7f) || (value > 0x1ff)) + { + snd_printk("i2c_write: invalid values.\n"); + return -EINVAL; + } + + tmp = reg << 25 | value << 16; + /* Not sure what this I2C channel controls. */ + /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */ + + /* This controls the I2C connected to the WM8775 ADC Codec */ + snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp); + + for(retry=0;retry<10;retry++) + { + /* Send the data to i2c */ + tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); + tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); + snd_ca0106_ptr_write(emu, I2C_A, 0, tmp); + + /* Wait till the transaction ends */ + while(1) + { + status = snd_ca0106_ptr_read(emu, I2C_A, 0); + //snd_printk("I2C:status=0x%x\n", status); + timeout++; + if((status & I2C_A_ADC_START)==0) + break; + + if(timeout>1000) + break; + } + //Read back and see if the transaction is successful + if((status & I2C_A_ADC_ABORT)==0) + break; + } + + if(retry==10) + { + snd_printk("Writing to ADC failed!\n"); + return -EINVAL; + } + + return 0; +} + + static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb) { unsigned long flags; @@ -259,11 +332,7 @@ static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb) static void snd_ca0106_pcm_free_substream(snd_pcm_runtime_t *runtime) { - ca0106_pcm_t *epcm = runtime->private_data; - - if (epcm) { - kfree(epcm); - } + kfree(runtime->private_data); } /* open_playback callback */ @@ -538,6 +607,61 @@ static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream) snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *epcm = runtime->private_data; int channel = epcm->channel_id; + u32 hcfg_mask = HCFG_CAPTURE_S32_LE; + u32 hcfg_set = 0x00000000; + u32 hcfg; + u32 over_sampling=0x2; + u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */ + u32 reg71_set = 0; + u32 reg71; + + //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1)); + //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base); + //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes); + /* reg71 controls ADC rate. */ + switch (runtime->rate) { + case 44100: + reg71_set = 0x00004000; + break; + case 48000: + reg71_set = 0; + break; + case 96000: + reg71_set = 0x00008000; + over_sampling=0xa; + break; + case 192000: + reg71_set = 0x0000c000; + over_sampling=0xa; + break; + default: + reg71_set = 0; + break; + } + /* Format is a global setting */ + /* FIXME: Only let the first channel accessed set this. */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + hcfg_set = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + hcfg_set = HCFG_CAPTURE_S32_LE; + break; + default: + hcfg_set = 0; + break; + } + hcfg = inl(emu->port + HCFG) ; + hcfg = (hcfg & ~hcfg_mask) | hcfg_set; + outl(hcfg, emu->port + HCFG); + reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); + reg71 = (reg71 & ~reg71_mask) | reg71_set; + snd_ca0106_ptr_write(emu, 0x71, 0, reg71); + if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ + snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */ + } + + //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1)); snd_ca0106_ptr_write(emu, 0x13, channel, 0); snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); @@ -810,6 +934,7 @@ static int snd_ca0106_ac97(ca0106_t *chip) memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; + ac97.scaps = AC97_SCAP_NO_SPDIF; return snd_ac97_mixer(pbus, &ac97, &chip->ac97); } @@ -993,6 +1118,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card, ca0106_t **rchip) { ca0106_t *chip; + ca0106_details_t *c; int err; int ch; static snd_device_ops_t ops = { @@ -1003,8 +1129,8 @@ static int __devinit snd_ca0106_create(snd_card_t *card, if ((err = pci_enable_device(pci)) < 0) return err; - if (pci_set_dma_mask(pci, 0xffffffffUL) < 0 || - pci_set_consistent_dma_mask(pci, 0xffffffffUL) < 0) { + if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { printk(KERN_ERR "error to set 32bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; @@ -1054,6 +1180,15 @@ static int __devinit snd_ca0106_create(snd_card_t *card, printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model, chip->revision, chip->serial); #endif + strcpy(card->driver, "CA0106"); + strcpy(card->shortname, "CA0106"); + + for (c=ca0106_chip_details; c->serial; c++) { + if (c->serial == chip->serial) break; + } + chip->details = c; + sprintf(card->longname, "%s at 0x%lx irq %i", + c->name, chip->port, chip->irq); outl(0, chip->port + INTE); @@ -1113,7 +1248,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card, //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */ /* Analog or Digital output */ snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf); - snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */ + snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */ chip->spdif_enable = 0; /* Set digital SPDIF output off */ chip->capture_source = 3; /* Set CAPTURE_SOURCE */ //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */ @@ -1138,13 +1273,11 @@ static int __devinit snd_ca0106_create(snd_card_t *card, snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */ chip->capture_source = 3; /* Set CAPTURE_SOURCE */ - if ((chip->serial == 0x10061102) || - (chip->serial == 0x10071102) || - (chip->serial == 0x10091462)) { /* The SB0410 and SB0413 use GPIO differently. */ + if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ outl(0x0, chip->port+GPIO); //outl(0x00f0e000, chip->port+GPIO); /* Analog */ - outl(0x005f4300, chip->port+GPIO); /* Analog */ + outl(0x005f5301, chip->port+GPIO); /* Analog */ } else { outl(0x0, chip->port+GPIO); outl(0x005f03a3, chip->port+GPIO); /* Analog */ @@ -1157,6 +1290,10 @@ static int __devinit snd_ca0106_create(snd_card_t *card, //outl(0x00000009, chip->port+HCFG); outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */ + if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ + snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_ca0106_free(chip); @@ -1172,7 +1309,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci, static int dev; snd_card_t *card; ca0106_t *chip; - ca0106_names_t *c; int err; if (dev >= SNDRV_CARDS) @@ -1207,9 +1343,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci, snd_card_free(card); return err; } - if ((chip->serial != 0x10061102) && - (chip->serial != 0x10071102) && - (chip->serial != 0x10091462) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */ + if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */ if ((err = snd_ca0106_ac97(chip)) < 0) { snd_card_free(card); return err; @@ -1222,15 +1356,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci, snd_ca0106_proc_init(chip); - strcpy(card->driver, "CA0106"); - strcpy(card->shortname, "CA0106"); - - for (c=ca0106_chip_names; c->serial; c++) { - if (c->serial == chip->serial) break; - } - sprintf(card->longname, "%s at 0x%lx irq %i", - c->name, chip->port, chip->irq); - if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; @@ -1267,7 +1392,7 @@ static int __init alsa_card_ca0106_init(void) { int err; - if ((err = pci_module_init(&driver)) > 0) + if ((err = pci_register_driver(&driver)) > 0) return err; return 0; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 97bed1b..0e5e9ce 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.16 + * Version: 0.0.17 * * FEATURES currently supported: * See ca0106_main.c for features. @@ -37,6 +37,8 @@ * Separated ca0106.c into separate functional .c files. * 0.0.16 * Modified Copyright message. + * 0.0.17 + * Implement Mic and Line in Capture. * * This code was initally based on code from ALSA's emu10k1x.c which is: * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> @@ -113,7 +115,7 @@ static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol, } else { /* Analog */ snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); - snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000); + snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000); mask = inl(emu->port + GPIO) | 0x101; @@ -183,6 +185,65 @@ static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata = .put = snd_ca0106_capture_source_put }; +static int snd_ca0106_capture_mic_line_in_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = { "Line in", "Mic in" }; + + 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_ca0106_capture_mic_line_in_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ca0106_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; + return 0; +} + +static int snd_ca0106_capture_mic_line_in_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ca0106_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + u32 tmp; + + val = ucontrol->value.enumerated.item[0] ; + change = (emu->capture_mic_line_in != val); + if (change) { + emu->capture_mic_line_in = val; + if (val) { + snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + tmp = inl(emu->port+GPIO) & ~0x400; + tmp = tmp | 0x400; + outl(tmp, emu->port+GPIO); + snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); + } else { + snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + tmp = inl(emu->port+GPIO) & ~0x400; + outl(tmp, emu->port+GPIO); + snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); + } + } + return change; +} + +static snd_kcontrol_new_t snd_ca0106_capture_mic_line_in __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic/Line in Capture", + .info = snd_ca0106_capture_mic_line_in_info, + .get = snd_ca0106_capture_mic_line_in_get, + .put = snd_ca0106_capture_mic_line_in_put +}; + static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; @@ -437,7 +498,7 @@ static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe = static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Unknown Volume", + .name = "Analog Side Volume", .info = snd_ca0106_volume_info, .get = snd_ca0106_volume_get_analog_unknown, .put = snd_ca0106_volume_put_analog_unknown @@ -620,10 +681,11 @@ int __devinit snd_ca0106_mixer(ca0106_t *emu) return -ENOMEM; if ((err = snd_ctl_add(card, kctl))) return err; - if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) { - /* already defined by ac97, remove it */ - /* FIXME: or do we need both controls? */ - remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT)); + if (emu->details->i2c_adc == 1) { + if ((kctl = snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; } if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL) return -ENOMEM; diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index afb7114..1c9cc82 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.17 + * Version: 0.0.18 * * FEATURES currently supported: * See ca0106_main.c for features. @@ -39,7 +39,9 @@ * Modified Copyright message. * 0.0.17 * Add iec958 file in proc file system to show status of SPDIF in. - * + * 0.0.18 + * Implement support for Line-in capture on SB Live 24bit. + * * This code was initally based on code from ALSA's emu10k1x.c which is: * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> * @@ -95,7 +97,7 @@ static struct snd_ca0106_category_str snd_ca0106_con_category[] = { }; -void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value) +static void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value) { int i; u32 status[4]; @@ -407,6 +409,20 @@ static void snd_ca0106_proc_reg_write(snd_info_entry_t *entry, } } +static void snd_ca0106_proc_i2c_write(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ca0106_t *emu = entry->private_data; + char line[64]; + unsigned int reg, val; + while (!snd_info_get_line(buffer, line, sizeof(line))) { + if (sscanf(line, "%x %x", ®, &val) != 2) + continue; + if ((reg <= 0x7f) || (val <= 0x1ff)) { + snd_ca0106_i2c_write(emu, reg, val); + } + } +} int __devinit snd_ca0106_proc_init(ca0106_t * emu) { @@ -418,6 +434,7 @@ int __devinit snd_ca0106_proc_init(ca0106_t * emu) snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32); entry->c.text.write_size = 64; entry->c.text.write = snd_ca0106_proc_reg_write32; + entry->mode |= S_IWUSR; } if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16); @@ -427,6 +444,14 @@ int __devinit snd_ca0106_proc_init(ca0106_t * emu) snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1); entry->c.text.write_size = 64; entry->c.text.write = snd_ca0106_proc_reg_write; + entry->mode |= S_IWUSR; +// entry->private_data = emu; + } + if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) { + snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write); + entry->c.text.write_size = 64; + entry->c.text.write = snd_ca0106_proc_i2c_write; + entry->mode |= S_IWUSR; // entry->private_data = emu; } if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 113208f..b450338 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -519,40 +519,50 @@ inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) } /* bit operations for dword register */ -static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +static int snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) { - unsigned int val; - val = inl(cm->iobase + cmd); + unsigned int val, oval; + val = oval = inl(cm->iobase + cmd); val |= flag; + if (val == oval) + return 0; outl(val, cm->iobase + cmd); + return 1; } -static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +static int snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) { - unsigned int val; - val = inl(cm->iobase + cmd); + unsigned int val, oval; + val = oval = inl(cm->iobase + cmd); val &= ~flag; + if (val == oval) + return 0; outl(val, cm->iobase + cmd); + return 1; } -#if 0 // not used /* bit operations for byte register */ -static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +static int snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) { - unsigned char val; - val = inb(cm->iobase + cmd); + unsigned char val, oval; + val = oval = inb(cm->iobase + cmd); val |= flag; + if (val == oval) + return 0; outb(val, cm->iobase + cmd); + return 1; } -static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +static int snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) { - unsigned char val; - val = inb(cm->iobase + cmd); + unsigned char val, oval; + val = oval = inb(cm->iobase + cmd); val &= ~flag; + if (val == oval) + return 0; outb(val, cm->iobase + cmd); + return 1; } -#endif /* @@ -2250,8 +2260,8 @@ DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* rever DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0); #endif DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0); -DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0); -DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0); +// DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0); +// DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0); // DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */ DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0); @@ -2300,10 +2310,114 @@ static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v } +static int snd_cmipci_line_in_mode_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static inline unsigned int get_line_in_mode(cmipci_t *cm) +{ + unsigned int val; + if (cm->chip_version >= 39) { + val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL); + if (val & CM_LINE_AS_BASS) + return 2; + } + val = snd_cmipci_read_b(cm, CM_REG_MIXER1); + if (val & CM_SPK4) + return 1; + return 0; +} + +static int snd_cmipci_line_in_mode_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&cm->reg_lock); + ucontrol->value.enumerated.item[0] = get_line_in_mode(cm); + spin_unlock_irq(&cm->reg_lock); + return 0; +} + +static int snd_cmipci_line_in_mode_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + int change; + + spin_lock_irq(&cm->reg_lock); + if (ucontrol->value.enumerated.item[0] == 2) + change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS); + else + change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS); + if (ucontrol->value.enumerated.item[0] == 1) + change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_SPK4); + else + change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_SPK4); + spin_unlock_irq(&cm->reg_lock); + return change; +} + +static int snd_cmipci_mic_in_mode_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + static char *texts[2] = { "Mic-In", "Center/LFE Output" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_cmipci_mic_in_mode_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + /* same bit as spdi_phase */ + spin_lock_irq(&cm->reg_lock); + ucontrol->value.enumerated.item[0] = + (snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0; + spin_unlock_irq(&cm->reg_lock); + return 0; +} + +static int snd_cmipci_mic_in_mode_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + int change; + + spin_lock_irq(&cm->reg_lock); + if (ucontrol->value.enumerated.item[0]) + change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE); + else + change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE); + spin_unlock_irq(&cm->reg_lock); + return change; +} + /* both for CM8338/8738 */ static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = { DEFINE_MIXER_SWITCH("Four Channel Mode", fourch), - DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear), + { + .name = "Line-In Mode", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_cmipci_line_in_mode_info, + .get = snd_cmipci_line_in_mode_get, + .put = snd_cmipci_line_in_mode_put, + }, }; /* for non-multichannel chips */ @@ -2341,10 +2455,15 @@ static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = { /* only for model 039 or later */ static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = { - DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass), DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2), DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2), - DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */ + { + .name = "Mic-In Mode", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_cmipci_mic_in_mode_info, + .get = snd_cmipci_mic_in_mode_get, + .put = snd_cmipci_mic_in_mode_put, + } }; /* card control switches */ @@ -2944,7 +3063,7 @@ static struct pci_driver driver = { static int __init alsa_card_cmipci_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_cmipci_exit(void) diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index d7e06b3..b6e1854 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -206,7 +206,10 @@ MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled)."); #define BA0_PMCS 0x0344 /* Power Management Control/Status */ #define BA0_CWPR 0x03e0 /* Configuration Write Protect */ + #define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */ +#define BA0_EPPMC_FPDN (1<<14) /* Full Power DowN */ + #define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */ #define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */ @@ -1461,6 +1464,11 @@ static int snd_cs4281_chip_init(cs4281_t *chip) int timeout; int retry_count = 2; + /* Having EPPMC.FPDN=1 prevent proper chip initialisation */ + tmp = snd_cs4281_peekBA0(chip, BA0_EPPMC); + if (tmp & BA0_EPPMC_FPDN) + snd_cs4281_pokeBA0(chip, BA0_EPPMC, tmp & ~BA0_EPPMC_FPDN); + __retry: tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); if (tmp != BA0_CFLR_DEFAULT) { @@ -2124,7 +2132,7 @@ static struct pci_driver driver = { static int __init alsa_card_cs4281_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_cs4281_exit(void) diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 25d6466..db212ec 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -171,7 +171,7 @@ static struct pci_driver driver = { static int __init alsa_card_cs46xx_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_cs46xx_exit(void) diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 5f2ffb7..fd4c50c 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1295,8 +1295,7 @@ static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime) { - cs46xx_pcm_t * cpcm = runtime->private_data; - kfree(cpcm); + kfree(runtime->private_data); } static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 6446afe..2085a99 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -228,7 +228,7 @@ static struct pci_driver driver = { static int __init alsa_card_emu10k1_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_emu10k1_exit(void) diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index c3c96f9..a341e75 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -170,7 +170,7 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); - if (emu->audigy && emu->revision == 4) { /* audigy2 */ + if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Hacks for Alice3 to work independent of haP16V driver */ u32 tmp; @@ -189,7 +189,7 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) /* Enabled Phased (8-channel) P16V playback */ outl(0x0201, emu->port + HCFG2); /* Set playback routing. */ - snd_emu10k1_ptr_write(emu, CAPTURE_P16V_SOURCE, 0, 78e4); + snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4); } if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */ /* Hacks for Alice3 to work independent of haP16V driver */ @@ -600,7 +600,7 @@ static int snd_emu10k1_free(emu10k1_t *emu) if (emu->port) pci_release_regions(emu->pci); pci_disable_device(emu->pci); - if (emu->audigy && emu->revision == 4) /* P16V */ + if (emu->card_capabilities->ca0151_chip) /* P16V */ snd_p16v_free(emu); kfree(emu); return 0; @@ -612,21 +612,24 @@ static int snd_emu10k1_dev_free(snd_device_t *device) return snd_emu10k1_free(emu); } -/* vendor, device, subsystem, emu10k1_chip, emu10k2_chip, ca0102_chip, ca0108_chip, ca0151_chip, spk71, spdif_bug, ac97_chip, ecard, driver, name */ - static emu_chip_details_t emu_chip_details[] = { /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102, .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0108_chip = 1, - .spk71 = 1} , + .spk71 = 1, + .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", + .id = "Audigy2", .emu10k2_chip = 1, - .ca0108_chip = 1} , + .ca0108_chip = 1, + .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, @@ -635,6 +638,7 @@ static emu_chip_details_t emu_chip_details[] = { .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102, .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, @@ -643,6 +647,7 @@ static emu_chip_details_t emu_chip_details[] = { .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102, .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, @@ -651,6 +656,7 @@ static emu_chip_details_t emu_chip_details[] = { .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102, .driver = "Audigy2", .name = "Audigy 2 [SB0240]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, @@ -659,35 +665,165 @@ static emu_chip_details_t emu_chip_details[] = { .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102, .driver = "Audigy2", .name = "Audigy 2 EX [1005]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, .spdif_bug = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102, .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", + .id = "Audigy2", .emu10k2_chip = 1, .ca0102_chip = 1, .ca0151_chip = 1, .spk71 = 1, .spdif_bug = 1, .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .revision = 0x04, + .driver = "Audigy2", .name = "Audigy 2 [Unknown]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10020052, + .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102, + .driver = "Audigy", .name = "Audigy 1 [SB0090]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102, + .driver = "Audigy", .name = "Audigy 1 [SB0090]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, - .driver = "Audigy", .name = "Audigy 1 or 2 [Unknown]", + .driver = "Audigy", .name = "Audigy 1 [Unknown]", + .id = "Audigy", .emu10k2_chip = 1, .ca0102_chip = 1, - .spdif_bug = 1} , + .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102, .driver = "EMU10K1", .name = "E-mu APS [4001]", + .id = "APS", .emu10k1_chip = 1, .ecard = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, + .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102, .driver = "EMU10K1", .name = "SB Live 5.1", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102, + .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", + .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102, + .driver = "EMU10K1", .name = "SBLive! [CT4620]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102, + .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0060]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102, + .driver = "EMU10K1", .name = "SBLive! [SB0105]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , {.vendor = 0x1102, .device = 0x0002, .driver = "EMU10K1", .name = "SB Live [Unknown]", + .id = "Live", .emu10k1_chip = 1, - .ac97_chip = 1} , + .ac97_chip = 1, + .sblive51 = 1} , { } /* terminator */ }; @@ -738,13 +874,15 @@ int __devinit snd_emu10k1_create(snd_card_t * card, emu->revision = revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model); - emu->card_type = EMU10K1_CARD_CREATIVE; snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model); for (c = emu_chip_details; c->vendor; c++) { if (c->vendor == pci->vendor && c->device == pci->device) { - if (c->subsystem == emu->serial) break; - if (c->subsystem == 0) break; + if (c->subsystem && c->subsystem != emu->serial) + continue; + if (c->revision && c->revision != emu->revision) + continue; + break; } } if (c->vendor == 0) { @@ -759,6 +897,23 @@ int __devinit snd_emu10k1_create(snd_card_t * card, else snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial); + if (!*card->id && c->id) { + int i, n = 0; + strlcpy(card->id, c->id, sizeof(card->id)); + for (;;) { + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id)) + break; + } + if (i >= snd_ecards_limit) + break; + n++; + if (n >= SNDRV_CARDS) + break; + snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n); + } + } + is_audigy = emu->audigy = c->emu10k2_chip; /* set the DMA transfer mask */ @@ -816,15 +971,6 @@ int __devinit snd_emu10k1_create(snd_card_t * card, pci_set_master(pci); - if (c->ecard) { - emu->card_type = EMU10K1_CARD_EMUAPS; - emu->APS = 1; - } - if (! c->ac97_chip) - emu->no_ac97 = 1; - - emu->spk71 = c->spk71; - emu->fx8010.fxbus_mask = 0x303f; if (extin_mask == 0) extin_mask = 0x3fcf; @@ -833,7 +979,7 @@ int __devinit snd_emu10k1_create(snd_card_t * card, emu->fx8010.extin_mask = extin_mask; emu->fx8010.extout_mask = extout_mask; - if (emu->APS) { + if (emu->card_capabilities->ecard) { if ((err = snd_emu10k1_ecard_init(emu)) < 0) { snd_emu10k1_free(emu); return err; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 27dfd8d..e90c5ddd 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -361,10 +361,7 @@ static void snd_emu10k1x_gpio_write(emu10k1x_t *emu, unsigned int value) static void snd_emu10k1x_pcm_free_substream(snd_pcm_runtime_t *runtime) { - emu10k1x_pcm_t *epcm = runtime->private_data; - - if (epcm) - kfree(epcm); + kfree(runtime->private_data); } static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice) @@ -1075,6 +1072,7 @@ static int __devinit snd_emu10k1x_proc_init(emu10k1x_t * emu) snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read); entry->c.text.write_size = 64; entry->c.text.write = snd_emu10k1x_proc_reg_write; + entry->mode |= S_IWUSR; entry->private_data = emu; } @@ -1627,7 +1625,7 @@ static int __init alsa_card_emu10k1x_init(void) { int err; - if ((err = pci_module_init(&driver)) > 0) + if ((err = pci_register_driver(&driver)) > 0) return err; return 0; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index b9fa2e8..0529fb2 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1077,7 +1077,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu) gpr += 2; /* PCM Side Playback (independent from stereo mix) */ - if (emu->spk71) { + if (emu->card_capabilities->spk71) { A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE)); A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE)); snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100); @@ -1145,14 +1145,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L); A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "CD Playback Volume" : "Audigy CD Playback Volume", + emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume", gpr, 0); gpr += 2; /* Audigy CD Capture Volume */ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L); A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "CD Capture Volume" : "Audigy CD Capture Volume", + emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume", gpr, 0); gpr += 2; @@ -1171,14 +1171,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L); A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "Line Playback Volume" : "Line2 Playback Volume", + emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume", gpr, 0); gpr += 2; /* Line2 Capture Volume */ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L); A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "Line Capture Volume" : "Line2 Capture Volume", + emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume", gpr, 0); gpr += 2; @@ -1197,14 +1197,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L); A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "Aux Playback Volume" : "Aux2 Playback Volume", + emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume", gpr, 0); gpr += 2; /* Aux2 Capture Volume */ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L); A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R); snd_emu10k1_init_stereo_control(&controls[nctl++], - emu->no_ac97 ? "Aux Capture Volume" : "Aux2 Capture Volume", + emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume", gpr, 0); gpr += 2; @@ -1232,7 +1232,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0); gpr++; - if (emu->spk71) { + if (emu->card_capabilities->spk71) { /* Stereo Mix Side Playback */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix)); A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1)); @@ -1266,7 +1266,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */ - if (emu->spk71) { + if (emu->card_capabilities->spk71) { A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */ } @@ -1359,7 +1359,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS); A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS); A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS); - if (emu->spk71) + if (emu->card_capabilities->spk71) A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS); /* headphone */ @@ -1982,22 +1982,27 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000); /* EFX capture - capture the 16 EXTINS */ - OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0)); - OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1)); - OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2)); - OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3)); - /* Dont connect anything to FXBUS2 1 and 2. These are shared with - * Center/LFE on the SBLive 5.1. The kX driver only changes the - * routing when it detects an SBLive 5.1. - * - * Since only 14 of the 16 EXTINs are used, this is not a big problem. - * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture - * 0 and 3, then the rest of the EXTINs to the corresponding FX capture - * channel. - */ - for (z = 4; z < 14; z++) { - OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z)); + if (emu->card_capabilities->sblive51) { + /* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER + * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording. + * + * Since only 14 of the 16 EXTINs are used, this is not a big problem. + * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture + * 0 and 3, then the rest of the EXTINs to the corresponding FX capture + * channel. Multitrack recorders will still see the center/lfe output signal + * on the second and third channels. + */ + OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0)); + OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1)); + OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2)); + OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3)); + for (z = 4; z < 14; z++) + OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z)); + } else { + for (z = 0; z < 16; z++) + OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z)); } + if (gpr > tmp) { snd_BUG(); @@ -2128,7 +2133,6 @@ static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info) int res; memset(info, 0, sizeof(info)); - info->card = emu->card_type; info->internal_tram_size = emu->fx8010.itram_size; info->external_tram_size = emu->fx8010.etram_pages.bytes / 2; fxbus = fxbuses; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 044663d..6be82c5 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -68,6 +68,7 @@ static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol, return 0; } +#if 0 static int snd_audigy_spdif_output_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[] = {"44100", "48000", "96000"}; @@ -152,6 +153,7 @@ static snd_kcontrol_new_t snd_audigy_spdif_output_rate = .get = snd_audigy_spdif_output_rate_get, .put = snd_audigy_spdif_output_rate_put }; +#endif static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) @@ -791,7 +793,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) NULL }; - if (!emu->no_ac97) { + if (emu->card_capabilities->ac97_chip) { ac97_bus_t *pbus; ac97_template_t ac97; static ac97_bus_ops_t ops = { @@ -833,7 +835,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) for (; *c; c++) remove_ctl(card, *c); } else { - if (emu->APS) + if (emu->card_capabilities->ecard) strcpy(emu->card->mixername, "EMU APS"); else if (emu->audigy) strcpy(emu->card->mixername, "SB Audigy"); @@ -918,7 +920,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) mix->attn[0] = 0xffff; } - if (! emu->APS) { /* FIXME: APS has these controls? */ + if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */ /* sb live! and audigy */ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) return -ENOMEM; @@ -935,18 +937,20 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) return -ENOMEM; if ((err = snd_ctl_add(card, kctl))) return err; +#if 0 if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL) return -ENOMEM; if ((err = snd_ctl_add(card, kctl))) return err; - } else if (! emu->APS) { +#endif + } else if (! emu->card_capabilities->ecard) { /* sb live! */ if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) return -ENOMEM; if ((err = snd_ctl_add(card, kctl))) return err; } - if (emu->audigy && emu->revision == 4) { /* P16V */ + if (emu->card_capabilities->ca0151_chip) { /* P16V */ if ((err = snd_p16v_mixer(emu))) return err; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index d1c2a02..520b99a 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -262,7 +262,7 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target) * * returns: cache invalidate size in samples */ -static int inline emu10k1_ccis(int stereo, int w_16) +static inline int emu10k1_ccis(int stereo, int w_16) { if (w_16) { return stereo ? 24 : 26; @@ -991,9 +991,7 @@ static void snd_emu10k1_pcm_efx_mixer_notify(emu10k1_t *emu, int idx, int activa static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) { - emu10k1_pcm_t *epcm = runtime->private_data; - - kfree(epcm); + kfree(runtime->private_data); } static int snd_emu10k1_efx_playback_close(snd_pcm_substream_t * substream) diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index d990d5e..cc22707 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -30,6 +30,7 @@ #include <linux/init.h> #include <sound/core.h> #include <sound/emu10k1.h> +#include "p16v.h" static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu, snd_info_buffer_t * buffer, @@ -44,28 +45,34 @@ static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu, unsigned int status, rate = 0; status = snd_emu10k1_ptr_read(emu, status_reg, 0); - if (rate_reg > 0) - rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); snd_iprintf(buffer, "\n%s\n", title); - snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); - snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); - snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); - snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); - snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); - snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); - snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy"); - snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16); - snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]); - snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]); - snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]); - - if (rate_reg > 0) { - snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); - snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); - snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE); + if (status != 0xffffffff) { + snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); + snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); + snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); + snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); + snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); + snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy"); + snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16); + snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]); + snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]); + snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]); + + if (rate_reg > 0) { + rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); + snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off"); + snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + /* From ((Rate * 48000 ) / 262144); */ + snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); + } + } else { + snd_iprintf(buffer, "No signal detected.\n"); } + } static void snd_emu10k1_proc_read(snd_info_entry_t *entry, @@ -182,7 +189,7 @@ static void snd_emu10k1_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, "EMU10K1\n\n"); snd_iprintf(buffer, "Card : %s\n", - emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); + emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative")); snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2); snd_iprintf(buffer, "\n"); @@ -223,15 +230,35 @@ static void snd_emu10k1_proc_read(snd_info_entry_t *entry, snd_iprintf(buffer, "\nAll FX Outputs :\n"); for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++) snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); - snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1); - snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1); - snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1); - snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS); - snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS); +} + +static void snd_emu10k1_proc_spdif_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + emu10k1_t *emu = entry->private_data; + snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS); + snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS); +#if 0 val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); snd_iprintf(buffer, "\nZoomed Video\n"); snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); +#endif +} + +static void snd_emu10k1_proc_rates_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 }; + emu10k1_t *emu = entry->private_data; + unsigned int val, tmp, n; + val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0); + tmp = (val >> 16) & 0x8; + for (n=0;n<4;n++) { + tmp = val >> (16 + (n*4)); + if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]); + else snd_iprintf(buffer, "Channel %d: No input\n", n); + } } static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, @@ -500,32 +527,46 @@ int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read); entry->c.text.write_size = 64; entry->c.text.write = snd_emu_proc_io_reg_write; + entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) { snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a); entry->c.text.write_size = 64; entry->c.text.write = snd_emu_proc_ptr_reg_write00; + entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) { snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b); entry->c.text.write_size = 64; entry->c.text.write = snd_emu_proc_ptr_reg_write00; + entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) { snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a); entry->c.text.write_size = 64; entry->c.text.write = snd_emu_proc_ptr_reg_write20; + entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) { snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b); entry->c.text.write_size = 64; entry->c.text.write = snd_emu_proc_ptr_reg_write20; + entry->mode |= S_IWUSR; } #endif if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read); + if (emu->card_capabilities->emu10k2_chip) { + if (! snd_card_proc_new(emu->card, "spdif-in", &entry)) + snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read); + } + if (emu->card_capabilities->ca0151_chip) { + if (! snd_card_proc_new(emu->card, "capture-rates", &entry)) + snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read); + } + if (! snd_card_proc_new(emu->card, "voices", &entry)) snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read); diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c index b81a7ca..cd8460d 100644 --- a/sound/pci/emu10k1/irq.c +++ b/sound/pci/emu10k1/irq.c @@ -37,7 +37,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) int handled = 0; while ((status = inl(emu->port + IPR)) != 0) { - // printk("irq - status = 0x%x\n", status); + //printk("emu10k1 irq - status = 0x%x\n", status); orig_status = status; handled = 1; if (status & IPR_PCIERROR) { @@ -147,9 +147,36 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); status &= ~IPR_FXDSP; } + if (status & IPR_P16V) { + while ((status2 = inl(emu->port + IPR2)) != 0) { + u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ + emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]); + emu10k1_voice_t *cvoice = &(emu->p16v_capture_voice); + + //printk(KERN_INFO "status2=0x%x\n", status2); + orig_status2 = status2; + if(status2 & mask) { + if(pvoice->use) { + snd_pcm_period_elapsed(pvoice->epcm->substream); + } else { + snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use); + } + } + if(status2 & 0x110000) { + //printk(KERN_INFO "capture int found\n"); + if(cvoice->use) { + //printk(KERN_INFO "capture period_elapsed\n"); + snd_pcm_period_elapsed(cvoice->epcm->substream); + } + } + outl(orig_status2, emu->port + IPR2); /* ack all */ + } + status &= ~IPR_P16V; + } + if (status) { unsigned int bits; - //snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status); + snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status); //make sure any interrupts we don't handle are disabled: bits = INTE_FXDSPENABLE | INTE_PCIERRORENABLE | @@ -170,20 +197,5 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) } outl(orig_status, emu->port + IPR); /* ack all */ } - if (emu->audigy && emu->revision == 4) { /* P16V */ - while ((status2 = inl(emu->port + IPR2)) != 0) { - u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ - emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]); - orig_status2 = status2; - if(status2 & mask) { - if(pvoice->use) { - snd_pcm_period_elapsed(pvoice->epcm->substream); - } else { - snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use); - } - } - outl(orig_status2, emu->port + IPR2); /* ack all */ - } - } return IRQ_RETVAL(handled); } diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index d03cb2f..98f9801 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -1,7 +1,7 @@ /* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * Driver p16v chips - * Version: 0.22 + * Version: 0.25 * * FEATURES currently supported: * Output fixed at S32_LE, 2 channel to hw:0,0 @@ -41,7 +41,15 @@ * Integrated with snd-emu10k1 driver. * 0.22 * Removed #if 0 ... #endif - * + * 0.23 + * Implement different capture rates. + * 0.24 + * Implement different capture source channels. + * e.g. When HD Capture source is set to SPDIF, + * setting HD Capture channel to 0 captures from CDROM digital input. + * setting HD Capture channel to 1 captures from SPDIF in. + * 0.25 + * Include capture buffer sizes. * * BUGS: * Some stability problems when unloading the snd-p16v kernel module. @@ -119,22 +127,41 @@ static snd_pcm_hardware_t snd_p16v_playback_hw = { SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */ - .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 , - .rate_min = 48000, + .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .rate_min = 44100, .rate_max = 192000, .channels_min = 8, .channels_max = 8, - .buffer_bytes_max = (32*1024), + .buffer_bytes_max = ((65536 - 64) * 8), .period_bytes_min = 64, - .period_bytes_max = (16*1024), + .period_bytes_max = (65536 - 64), .periods_min = 2, .periods_max = 8, .fifo_size = 0, }; +static snd_pcm_hardware_t snd_p16v_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (65536 - 64), + .period_bytes_min = 64, + .period_bytes_max = (65536 - 128) >> 1, /* size has to be N*64 bytes */ + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + static void snd_p16v_pcm_free_substream(snd_pcm_runtime_t *runtime) { - snd_pcm_t *epcm = runtime->private_data; + emu10k1_pcm_t *epcm = runtime->private_data; if (epcm) { //snd_printk("epcm free: %p\n", epcm); @@ -178,15 +205,63 @@ static int snd_p16v_pcm_open_playback_channel(snd_pcm_substream_t *substream, in return 0; } +/* open_capture callback */ +static int snd_p16v_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_voice_t *channel = &(emu->p16v_capture_voice); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); + //snd_printk("epcm kcalloc: %p\n", epcm); + + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->substream = substream; + //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id); + + runtime->private_data = epcm; + runtime->private_free = snd_p16v_pcm_free_substream; + + runtime->hw = snd_p16v_capture_hw; + + channel->emu = emu; + channel->number = channel_id; + + channel->use=1; + //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use); + //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel); + //channel->interrupt = snd_p16v_pcm_channel_interrupt; + channel->epcm=epcm; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + return 0; +} + /* close callback */ static int snd_p16v_pcm_close_playback(snd_pcm_substream_t *substream) { emu10k1_t *emu = snd_pcm_substream_chip(substream); //snd_pcm_runtime_t *runtime = substream->runtime; - //emu10k1_pcm_t *epcm = runtime->private_data; - emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0; -/* FIXME: maybe zero others */ + //emu10k1_pcm_t *epcm = runtime->private_data; + emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0; + /* FIXME: maybe zero others */ + return 0; +} + +/* close callback */ +static int snd_p16v_pcm_close_capture(snd_pcm_substream_t *substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + //snd_pcm_runtime_t *runtime = substream->runtime; + //emu10k1_pcm_t *epcm = runtime->private_data; + emu->p16v_capture_voice.use=0; + /* FIXME: maybe zero others */ return 0; } @@ -195,36 +270,55 @@ static int snd_p16v_pcm_open_playback_front(snd_pcm_substream_t *substream) return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL); } +static int snd_p16v_pcm_open_capture(snd_pcm_substream_t *substream) +{ + // Only using channel 0 for now, but the card has 2 channels. + return snd_p16v_pcm_open_capture_channel(substream, 0); +} + /* hw_params callback */ static int snd_p16v_pcm_hw_params_playback(snd_pcm_substream_t *substream, snd_pcm_hw_params_t * hw_params) { int result; - //snd_printk("hw_params alloc: substream=%p\n", substream); result = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); - //snd_printk("hw_params alloc: result=%d\n", result); - //dump_stack(); return result; } +/* hw_params callback */ +static int snd_p16v_pcm_hw_params_capture(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t * hw_params) +{ + int result; + result = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + return result; +} + + /* hw_free callback */ static int snd_p16v_pcm_hw_free_playback(snd_pcm_substream_t *substream) { int result; - //snd_printk("hw_params free: substream=%p\n", substream); result = snd_pcm_lib_free_pages(substream); - //snd_printk("hw_params free: result=%d\n", result); - //dump_stack(); return result; } +/* hw_free callback */ +static int snd_p16v_pcm_hw_free_capture(snd_pcm_substream_t *substream) +{ + int result; + result = snd_pcm_lib_free_pages(substream); + return result; +} + + /* prepare playback callback */ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream) { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - //emu10k1_pcm_t *epcm = runtime->private_data; int channel = substream->pcm->device - emu->p16v_device_offset; u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel)); u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); @@ -237,23 +331,21 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream) tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel); switch (runtime->rate) { case 44100: - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x8000); /* FIXME: This will change the capture rate as well! */ - break; - case 48000: - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x0000); /* FIXME: This will change the capture rate as well! */ + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080); break; case 96000: - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x4000); /* FIXME: This will change the capture rate as well! */ + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040); break; case 192000: - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x2000); /* FIXME: This will change the capture rate as well! */ + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020); break; + case 48000: default: - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, 0x0000); /* FIXME: This will change the capture rate as well! */ + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000); break; } /* FIXME: Check emu->buffer.size before actually writing to it. */ - for(i=0; i < runtime->periods; i++) { + for(i=0; i < runtime->periods; i++) { table_base[i*2]=runtime->dma_addr+(i*period_size_bytes); table_base[(i*2)+1]=period_size_bytes<<16; } @@ -262,7 +354,8 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream) snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19); snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0); snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr); - snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes + //snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes + snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0); snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0); snd_emu10k1_ptr20_write(emu, 0x08, channel, 0); @@ -270,6 +363,41 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream) return 0; } +/* prepare capture callback */ +static int snd_p16v_pcm_prepare_capture(snd_pcm_substream_t *substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int channel = substream->pcm->device - emu->p16v_device_offset; + u32 tmp; + //printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1)); + tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel); + switch (runtime->rate) { + case 44100: + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800); + break; + case 96000: + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400); + break; + case 192000: + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200); + break; + case 48000: + default: + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000); + break; + } + /* FIXME: Check emu->buffer.size before actually writing to it. */ + snd_emu10k1_ptr20_write(emu, 0x13, channel, 0); + snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); + snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes + snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0); + //snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */ + //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel)); + + return 0; +} + static void snd_p16v_intr_enable(emu10k1_t *emu, unsigned int intrenb) { unsigned long flags; @@ -345,6 +473,36 @@ static int snd_p16v_pcm_trigger_playback(snd_pcm_substream_t *substream, return result; } +/* trigger_capture callback */ +static int snd_p16v_pcm_trigger_capture(snd_pcm_substream_t *substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = runtime->private_data; + int channel = 0; + int result = 0; + u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_p16v_intr_enable(emu, inte); + snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel)); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel)); + snd_p16v_intr_disable(emu, inte); + //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel)); + epcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + return result; +} + /* pointer_playback callback */ static snd_pcm_uframes_t snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream) @@ -370,6 +528,31 @@ snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream) return ptr; } +/* pointer_capture callback */ +static snd_pcm_uframes_t +snd_p16v_pcm_pointer_capture(snd_pcm_substream_t *substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = runtime->private_data; + snd_pcm_uframes_t ptr, ptr1, ptr2 = 0; + int channel = 0; + + if (!epcm->running) + return 0; + + ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel); + ptr2 = bytes_to_frames(runtime, ptr1); + ptr=ptr2; + if (ptr >= runtime->buffer_size) { + ptr -= runtime->buffer_size; + printk("buffer capture limited!\n"); + } + //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); + + return ptr; +} + /* operators */ static snd_pcm_ops_t snd_p16v_playback_front_ops = { .open = snd_p16v_pcm_open_playback_front, @@ -382,6 +565,18 @@ static snd_pcm_ops_t snd_p16v_playback_front_ops = { .pointer = snd_p16v_pcm_pointer_playback, }; +static snd_pcm_ops_t snd_p16v_capture_ops = { + .open = snd_p16v_pcm_open_capture, + .close = snd_p16v_pcm_close_capture, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_p16v_pcm_hw_params_capture, + .hw_free = snd_p16v_pcm_hw_free_capture, + .prepare = snd_p16v_pcm_prepare_capture, + .trigger = snd_p16v_pcm_trigger_capture, + .pointer = snd_p16v_pcm_pointer_capture, +}; + + int snd_p16v_free(emu10k1_t *chip) { // release the data @@ -405,20 +600,22 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm) snd_pcm_t *pcm; snd_pcm_substream_t *substream; int err; - int capture=0; + int capture=1; //snd_printk("snd_p16v_pcm called. device=%d\n", device); emu->p16v_device_offset = device; if (rpcm) *rpcm = NULL; - //if (device == 0) capture=1; + if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_p16v_pcm_free; - + // Single playback 8 channel device. + // Single capture 2 channel device. snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; @@ -431,7 +628,7 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm) if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), - 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */ + ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0) return err; //snd_printk("preallocate playback substream: err=%d\n", err); } @@ -442,7 +639,7 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm) if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), - 64*1024, 64*1024)) < 0) + 65536 - 64, 65536 - 64)) < 0) return err; //snd_printk("preallocate capture substream: err=%d\n", err); } @@ -694,6 +891,106 @@ static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear = .put = snd_p16v_volume_put_spdif_rear }; +static int snd_p16v_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S", "CDIF", "FX", "AC97" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + 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_p16v_capture_source_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->p16v_capture_source; + return 0; +} + +static int snd_p16v_capture_source_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + u32 mask; + u32 source; + + val = ucontrol->value.enumerated.item[0] ; + change = (emu->p16v_capture_source != val); + if (change) { + emu->p16v_capture_source = val; + source = (val << 28) | (val << 24) | (val << 20) | (val << 16); + mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff; + snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask); + } + return change; +} + +static snd_kcontrol_new_t snd_p16v_capture_source __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "HD Capture source", + .info = snd_p16v_capture_source_info, + .get = snd_p16v_capture_source_get, + .put = snd_p16v_capture_source_put +}; + +static int snd_p16v_capture_channel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "0", "1", "2", "3", }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_p16v_capture_channel_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel; + return 0; +} + +static int snd_p16v_capture_channel_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + u32 tmp; + + val = ucontrol->value.enumerated.item[0] ; + change = (emu->p16v_capture_channel != val); + if (change) { + emu->p16v_capture_channel = val; + tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc; + snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val); + } + return change; +} + +static snd_kcontrol_new_t snd_p16v_capture_channel __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "HD Capture channel", + .info = snd_p16v_capture_channel_info, + .get = snd_p16v_capture_channel_get, + .put = snd_p16v_capture_channel_put +}; + int snd_p16v_mixer(emu10k1_t *emu) { int err; @@ -731,6 +1028,14 @@ int snd_p16v_mixer(emu10k1_t *emu) return -ENOMEM; if ((err = snd_ctl_add(card, kctl))) return err; + if ((kctl = snd_ctl_new1(&snd_p16v_capture_source, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&snd_p16v_capture_channel, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; return 0; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index f910399..4e63498 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2401,7 +2401,7 @@ static struct pci_driver driver = { static int __init alsa_card_ens137x_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_ens137x_exit(void) diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index b4ca8ad..b492777 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1761,7 +1761,7 @@ static struct pci_driver driver = { static int __init alsa_card_es1938_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_es1938_exit(void) diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index faf63ff..ea889b3 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2559,6 +2559,7 @@ static struct ess_device_list pm_whitelist[] __devinitdata = { { TYPE_MAESTRO2E, 0x103c }, { TYPE_MAESTRO2E, 0x1179 }, { TYPE_MAESTRO2E, 0x14c0 }, /* HP omnibook 4150 */ + { TYPE_MAESTRO2E, 0x1558 }, }; static struct ess_device_list mpu_blacklist[] __devinitdata = { @@ -2795,7 +2796,7 @@ static struct pci_driver driver = { static int __init alsa_card_es1968_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_es1968_exit(void) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 08e7c5a..ff10e63 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -195,6 +195,7 @@ struct _snd_fm801 { static struct pci_device_id snd_fm801_ids[] = { { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */ + { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */ { 0, } }; @@ -1468,7 +1469,7 @@ static struct pci_driver driver = { static int __init alsa_card_fm801_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_fm801_exit(void) diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 570a59d..bd8cb33 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o +snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9ed117a..e2cf023 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -49,8 +49,10 @@ struct hda_vendor_id { /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { { 0x10ec, "Realtek" }, + { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x434d, "C-Media" }, + { 0x8384, "SigmaTel" }, {} /* terminator */ }; @@ -508,7 +510,7 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, /* FIXME: support for multiple AFGs? */ codec->afg = look_for_afg_node(codec); if (! codec->afg) { - snd_printk(KERN_ERR "hda_codec: no AFG node found\n"); + snd_printdd("hda_codec: no AFG node found\n"); snd_hda_codec_free(codec); return -ENODEV; } @@ -548,6 +550,9 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format) { + if (! nid) + return; + snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, @@ -561,9 +566,10 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stre * amp access functions */ -#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64) +/* FIXME: more better hash key? */ +#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #define INFO_AMP_CAPS (1<<0) -#define INFO_AMP_VOL (1<<1) +#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) /* initialize the hash table */ static void init_amp_hash(struct hda_codec *codec) @@ -622,28 +628,29 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) /* * read the current volume to info - * if the cache exists, read from the cache. + * if the cache exists, read the cache value. */ -static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, +static unsigned int get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index) { u32 val, parm; - if (info->status & (INFO_AMP_VOL << ch)) - return; + if (info->status & INFO_AMP_VOL(ch)) + return info->vol[ch]; parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; parm |= index; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); info->vol[ch] = val & 0xff; - info->status |= INFO_AMP_VOL << ch; + info->status |= INFO_AMP_VOL(ch); + return info->vol[ch]; } /* - * write the current volume in info to the h/w + * write the current volume in info to the h/w and update the cache */ -static void put_vol_mute(struct hda_codec *codec, +static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, int val) { u32 parm; @@ -653,30 +660,34 @@ static void put_vol_mute(struct hda_codec *codec, parm |= index << AC_AMP_SET_INDEX_SHIFT; parm |= val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); + info->vol[ch] = val; } /* - * read/write AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. + * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) +static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) { struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); if (! info) return 0; - get_vol_mute(codec, info, nid, ch, direction, index); - return info->vol[ch]; + return get_vol_mute(codec, info, nid, ch, direction, index); } -int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val) +/* + * update the AMP value, mask = bit mask to set, val = the value + */ +static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) { struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); + if (! info) return 0; - get_vol_mute(codec, info, nid, ch, direction, idx); + val &= mask; + val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; if (info->vol[ch] == val && ! codec->in_resume) return 0; - put_vol_mute(codec, nid, ch, direction, idx, val); - info->vol[ch] = val; + put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } @@ -735,21 +746,15 @@ int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); - int val; long *valp = ucontrol->value.integer.value; int change = 0; - if (chs & 1) { - val = *valp & 0x7f; - val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80; - change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); - valp++; - } - if (chs & 2) { - val = *valp & 0x7f; - val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80; - change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); - } + if (chs & 1) + change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, + 0x7f, *valp); + if (chs & 2) + change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, + 0x7f, valp[1]); return change; } @@ -788,21 +793,15 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); - int val; long *valp = ucontrol->value.integer.value; int change = 0; - if (chs & 1) { - val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f; - val |= *valp ? 0 : 0x80; - change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); - valp++; - } - if (chs & 2) { - val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; - val |= *valp ? 0 : 0x80; - change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); - } + if (chs & 1) + change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, + 0x80, *valp ? 0 : 0x80); + if (chs & 2) + change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, + 0x80, valp[1] ? 0 : 0x80); return change; } @@ -1448,10 +1447,6 @@ static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream snd_assert(info->nid, return -EINVAL); info->ops.prepare = hda_pcm_default_prepare; } - if (info->ops.prepare == NULL) { - snd_assert(info->nid, return -EINVAL); - info->ops.prepare = hda_pcm_default_prepare; - } if (info->ops.cleanup == NULL) { snd_assert(info->nid, return -EINVAL); info->ops.cleanup = hda_pcm_default_cleanup; @@ -1525,12 +1520,12 @@ int snd_hda_build_pcms(struct hda_bus *bus) * * If no entries are matching, the function returns a negative value. */ -int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl) +int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl) { - struct hda_board_config *c; + const struct hda_board_config *c; if (codec->bus->modelname) { - for (c = tbl; c->modelname || c->pci_vendor; c++) { + for (c = tbl; c->modelname || c->pci_subvendor; c++) { if (c->modelname && ! strcmp(codec->bus->modelname, c->modelname)) { snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname); @@ -1543,9 +1538,10 @@ int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config u16 subsystem_vendor, subsystem_device; pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device); - for (c = tbl; c->modelname || c->pci_vendor; c++) { - if (c->pci_vendor == subsystem_vendor && - c->pci_device == subsystem_device) + for (c = tbl; c->modelname || c->pci_subvendor; c++) { + if (c->pci_subvendor == subsystem_vendor && + (! c->pci_subdevice /* all match */|| + (c->pci_subdevice == subsystem_device))) return c->config; } } @@ -1687,11 +1683,12 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); /* surrounds */ for (i = 1; i < mout->num_dacs; i++) { - if (i == HDA_REAR && chs == 2) /* copy front to rear */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); - else if (chs >= (i + 1) * 2) /* independent out */ + if (chs >= (i + 1) * 2) /* independent out */ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2, format); + else /* copy front */ + snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, + format); } return 0; } @@ -1717,6 +1714,105 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_o return 0; } +/* + * Helper for automatic ping configuration + */ +/* parse all pin widgets and store the useful pin nids to cfg */ +int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg) +{ + hda_nid_t nid, nid_start; + int i, j, nodes; + short seq, sequences[4], assoc_line_out; + + memset(cfg, 0, sizeof(*cfg)); + + memset(sequences, 0, sizeof(sequences)); + assoc_line_out = 0; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); + for (nid = nid_start; nid < nodes + nid_start; nid++) { + unsigned int wid_caps = snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + unsigned int def_conf; + short assoc, loc; + + /* read all default configuration for pin complex */ + if (wid_type != AC_WID_PIN) + continue; + def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + loc = get_defcfg_location(def_conf); + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + if (! assoc) + continue; + if (! assoc_line_out) + assoc_line_out = assoc; + else if (assoc_line_out != assoc) + continue; + if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) + continue; + cfg->line_out_pins[cfg->line_outs] = nid; + sequences[cfg->line_outs] = seq; + cfg->line_outs++; + break; + case AC_JACK_HP_OUT: + cfg->hp_pin = nid; + break; + case AC_JACK_MIC_IN: + if (loc == AC_JACK_LOC_FRONT) + cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid; + else + cfg->input_pins[AUTO_PIN_MIC] = nid; + break; + case AC_JACK_LINE_IN: + if (loc == AC_JACK_LOC_FRONT) + cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; + else + cfg->input_pins[AUTO_PIN_LINE] = nid; + break; + case AC_JACK_CD: + cfg->input_pins[AUTO_PIN_CD] = nid; + break; + case AC_JACK_AUX: + cfg->input_pins[AUTO_PIN_AUX] = nid; + break; + case AC_JACK_SPDIF_OUT: + cfg->dig_out_pin = nid; + break; + case AC_JACK_SPDIF_IN: + cfg->dig_in_pin = nid; + break; + } + } + + /* sort by sequence */ + for (i = 0; i < cfg->line_outs; i++) + for (j = i + 1; j < cfg->line_outs; j++) + if (sequences[i] > sequences[j]) { + seq = sequences[i]; + sequences[i] = sequences[j]; + sequences[j] = seq; + nid = cfg->line_out_pins[i]; + cfg->line_out_pins[i] = cfg->line_out_pins[j]; + cfg->line_out_pins[j] = nid; + } + + /* Swap surround and CLFE: the association order is front/CLFE/surr/back */ + if (cfg->line_outs >= 3) { + nid = cfg->line_out_pins[1]; + cfg->line_out_pins[1] = cfg->line_out_pins[2]; + cfg->line_out_pins[2] = nid; + } + + return 0; +} + #ifdef CONFIG_PM /* * power management diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c9e9dc9..5999156 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -75,6 +75,9 @@ enum { #define AC_VERB_GET_DIGI_CONVERT 0x0f0d #define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f /* f10-f1a: GPIO */ +#define AC_VERB_GET_GPIO_DATA 0x0f15 +#define AC_VERB_GET_GPIO_MASK 0x0f16 +#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 #define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c /* @@ -97,6 +100,9 @@ enum { #define AC_VERB_SET_DIGI_CONVERT_1 0x70d #define AC_VERB_SET_DIGI_CONVERT_2 0x70e #define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPIO_DATA 0x715 +#define AC_VERB_SET_GPIO_MASK 0x716 +#define AC_VERB_SET_GPIO_DIRECTION 0x717 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e @@ -176,16 +182,15 @@ enum { #define AC_PINCAP_OUT (1<<4) /* output capable */ #define AC_PINCAP_IN (1<<5) /* input capable */ #define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ -#define AC_PINCAP_VREF (7<<8) +#define AC_PINCAP_VREF (0x37<<8) #define AC_PINCAP_VREF_SHIFT 8 #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ -/* Vref status (used in pin cap and pin ctl) */ -#define AC_PIN_VREF_HIZ (1<<0) /* Hi-Z */ -#define AC_PIN_VREF_50 (1<<1) /* 50% */ -#define AC_PIN_VREF_GRD (1<<2) /* ground */ -#define AC_PIN_VREF_80 (1<<4) /* 80% */ -#define AC_PIN_VREF_100 (1<<5) /* 100% */ - +/* Vref status (used in pin cap) */ +#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ +#define AC_PINCAP_VREF_50 (1<<1) /* 50% */ +#define AC_PINCAP_VREF_GRD (1<<2) /* ground */ +#define AC_PINCAP_VREF_80 (1<<4) /* 80% */ +#define AC_PINCAP_VREF_100 (1<<5) /* 100% */ /* Amplifier capabilities */ #define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */ @@ -248,6 +253,11 @@ enum { /* Pin widget control - 8bit */ #define AC_PINCTL_VREFEN (0x7<<0) +#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ +#define AC_PINCTL_VREF_50 1 /* 50% */ +#define AC_PINCTL_VREF_GRD 2 /* ground */ +#define AC_PINCTL_VREF_80 4 /* 80% */ +#define AC_PINCTL_VREF_100 5 /* 100% */ #define AC_PINCTL_IN_EN (1<<5) #define AC_PINCTL_OUT_EN (1<<6) #define AC_PINCTL_HP_EN (1<<7) @@ -255,7 +265,9 @@ enum { /* configuration default - 32bit */ #define AC_DEFCFG_SEQUENCE (0xf<<0) #define AC_DEFCFG_DEF_ASSOC (0xf<<4) +#define AC_DEFCFG_ASSOC_SHIFT 4 #define AC_DEFCFG_MISC (0xf<<8) +#define AC_DEFCFG_MISC_SHIFT 8 #define AC_DEFCFG_COLOR (0xf<<12) #define AC_DEFCFG_COLOR_SHIFT 12 #define AC_DEFCFG_CONN_TYPE (0xf<<16) @@ -413,7 +425,7 @@ struct hda_bus { /* codec linked list */ struct list_head codec_list; - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */ + struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; /* caddr -> codec */ struct semaphore cmd_mutex; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 69f7b6c..2d046ab 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -44,7 +44,7 @@ struct hda_gnode { struct list_head list; }; -/* pathc-specific record */ +/* patch-specific record */ struct hda_gspec { struct hda_gnode *dac_node; /* DAC node */ struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */ @@ -68,8 +68,8 @@ struct hda_gspec { /* * retrieve the default device type from the default config value */ -#define get_defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) -#define get_defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) /* * destructor @@ -323,7 +323,7 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec, if (! (node->pin_caps & AC_PINCAP_OUT)) continue; if (jack_type >= 0) { - if (jack_type != get_defcfg_type(node)) + if (jack_type != defcfg_type(node)) continue; if (node->wid_caps & AC_WCAP_DIGITAL) continue; /* skip SPDIF */ @@ -418,15 +418,15 @@ static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc */ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) { - unsigned int location = get_defcfg_location(node); - switch (get_defcfg_type(node)) { + unsigned int location = defcfg_location(node); + switch (defcfg_type(node)) { case AC_JACK_LINE_IN: if ((location & 0x0f) == AC_JACK_LOC_FRONT) return "Front Line"; return "Line"; case AC_JACK_CD: if (pinctl) - *pinctl |= AC_PIN_VREF_GRD; + *pinctl |= AC_PINCTL_VREF_GRD; return "CD"; case AC_JACK_AUX: if ((location & 0x0f) == AC_JACK_LOC_FRONT) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 959953c..5e0cca3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -51,6 +51,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static char *model[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -60,12 +61,17 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); +module_param_array(position_fix, int, NULL, 0444); +MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF)."); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH6M}," "{Intel, ICH7}," - "{Intel, ESB2}}"); + "{Intel, ESB2}," + "{ATI, SB450}," + "{VIA, VT8251}," + "{VIA, VT8237A}}"); MODULE_DESCRIPTION("Intel HDA driver"); #define SFX "hda-intel: " @@ -150,7 +156,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* STATESTS int mask: SD2,SD1,SD0 */ #define STATESTS_INT_MASK 0x07 -#define AZX_MAX_CODECS 3 +#define AZX_MAX_CODECS 4 /* SD_CTL bits */ #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ @@ -183,6 +189,18 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define ICH6_MAX_CORB_ENTRIES 256 #define ICH6_MAX_RIRB_ENTRIES 256 +/* position fix mode */ +enum { + POS_FIX_FIFO, + POS_FIX_NONE, + POS_FIX_POSBUF +}; + +/* Defines for ATI HD Audio support in SB450 south bridge */ +#define ATI_SB450_HDAUDIO_PCI_DEVICE_ID 0x437b +#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 +#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 + /* * Use CORB/RIRB for communication from/to codecs. @@ -191,12 +209,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define USE_CORB_RIRB /* - * Define this if use the position buffer instead of reading SD_LPIB - * It's not used as default since SD_LPIB seems to give more accurate position - */ -/* #define USE_POSBUF */ - -/* */ typedef struct snd_azx azx_t; @@ -271,6 +283,10 @@ struct snd_azx { struct snd_dma_buffer bdl; struct snd_dma_buffer rb; struct snd_dma_buffer posbuf; + + /* flags */ + int position_fix; + unsigned int initialized: 1; }; /* @@ -638,7 +654,7 @@ static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev) */ static void azx_init_chip(azx_t *chip) { - unsigned char tcsel_reg; + unsigned char tcsel_reg, ati_misc_cntl2; /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) * TCSEL == Traffic Class Select Register, which sets PCI express QOS @@ -657,11 +673,20 @@ static void azx_init_chip(azx_t *chip) /* initialize the codec command I/O */ azx_init_cmd_io(chip); -#ifdef USE_POSBUF - /* program the position buffer */ - azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); - azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr)); -#endif + if (chip->position_fix == POS_FIX_POSBUF) { + /* program the position buffer */ + azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); + azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr)); + } + + /* For ATI SB450 azalia HD audio, we need to enable snoop */ + if (chip->pci->vendor == PCI_VENDOR_ID_ATI && + chip->pci->device == ATI_SB450_HDAUDIO_PCI_DEVICE_ID) { + pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, + &ati_misc_cntl2); + pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, + (ati_misc_cntl2 & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP); + } } @@ -791,11 +816,12 @@ static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev) /* upper BDL address */ azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); -#ifdef USE_POSBUF - /* enable the position buffer */ - if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); -#endif + if (chip->position_fix == POS_FIX_POSBUF) { + /* enable the position buffer */ + if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); + } + /* set the interrupt enable bits in the descriptor control register */ azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); @@ -1036,16 +1062,20 @@ static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd) static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream) { + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); unsigned int pos; -#ifdef USE_POSBUF - /* use the position buffer */ - pos = *azx_dev->posbuf; -#else - /* read LPIB */ - pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size; -#endif + if (chip->position_fix == POS_FIX_POSBUF) { + /* use the position buffer */ + pos = *azx_dev->posbuf; + } else { + /* read LPIB */ + pos = azx_sd_readl(azx_dev, SD_LPIB); + if (chip->position_fix == POS_FIX_FIFO) + pos += azx_dev->fifo_size; + } if (pos >= azx_dev->bufsize) pos = 0; return bytes_to_frames(substream->runtime, pos); @@ -1155,9 +1185,8 @@ static int __devinit azx_init_stream(azx_t *chip) azx_dev_t *azx_dev = &chip->azx_dev[i]; azx_dev->bdl = (u32 *)(chip->bdl.area + off); azx_dev->bdl_addr = chip->bdl.addr + off; -#ifdef USE_POSBUF - azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); -#endif + if (chip->position_fix == POS_FIX_POSBUF) + azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ @@ -1207,7 +1236,7 @@ static int azx_resume(snd_card_t *card) */ static int azx_free(azx_t *chip) { - if (chip->remap_addr) { + if (chip->initialized) { int i; for (i = 0; i < MAX_ICH6_DEV; i++) @@ -1237,10 +1266,8 @@ static int azx_free(azx_t *chip) snd_dma_free_pages(&chip->bdl); if (chip->rb.area) snd_dma_free_pages(&chip->rb); -#ifdef USE_POSBUF if (chip->posbuf.area) snd_dma_free_pages(&chip->posbuf); -#endif pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); @@ -1256,7 +1283,8 @@ static int azx_dev_free(snd_device_t *device) /* * constructor */ -static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip) +static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, + int posfix, azx_t **rchip) { azx_t *chip; int err = 0; @@ -1283,6 +1311,8 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r chip->pci = pci; chip->irq = -1; + chip->position_fix = posfix; + if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { kfree(chip); pci_disable_device(pci); @@ -1314,14 +1344,14 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); goto errout; } -#ifdef USE_POSBUF - /* allocate memory for the position buffer */ - if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) { - snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); - goto errout; + if (chip->position_fix == POS_FIX_POSBUF) { + /* allocate memory for the position buffer */ + if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) { + snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); + goto errout; + } } -#endif /* allocate CORB/RIRB */ if ((err = azx_alloc_cmd_io(chip)) < 0) goto errout; @@ -1332,6 +1362,8 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r /* initialize chip */ azx_init_chip(chip); + chip->initialized = 1; + /* codec detection */ if (! chip->codec_mask) { snd_printk(KERN_ERR SFX "no codecs found!\n"); @@ -1372,7 +1404,7 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id * return -ENOMEM; } - if ((err = azx_create(card, pci, &chip)) < 0) { + if ((err = azx_create(card, pci, position_fix[dev], &chip)) < 0) { snd_card_free(card); return err; } @@ -1424,6 +1456,9 @@ static struct pci_device_id azx_ids[] = { { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */ { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */ { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ESB2 */ + { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ATI SB450 */ + { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* VIA VT8251/VT8237A */ + { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ALI 5461? */ { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); @@ -1439,7 +1474,7 @@ static struct pci_driver driver = { static int __init alsa_card_azx_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_azx_exit(void) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7c7b849..810cfd2 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -126,11 +126,11 @@ static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } struct hda_board_config { const char *modelname; int config; - unsigned short pci_vendor; - unsigned short pci_device; + unsigned short pci_subvendor; + unsigned short pci_subdevice; }; -int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl); +int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl); int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew); /* @@ -158,4 +158,35 @@ struct hda_bus_unsolicited { struct work_struct work; }; +/* + * Helper for automatic ping configuration + */ + +enum { + AUTO_PIN_MIC, + AUTO_PIN_FRONT_MIC, + AUTO_PIN_LINE, + AUTO_PIN_FRONT_LINE, + AUTO_PIN_CD, + AUTO_PIN_AUX, + AUTO_PIN_LAST +}; + +struct auto_pin_cfg { + int line_outs; + hda_nid_t line_out_pins[4]; /* sorted in the order of Front/Surr/CLFE/Side */ + hda_nid_t hp_pin; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t dig_out_pin; + hda_nid_t dig_in_pin; +}; + +#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) +#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) +#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE) +#define get_defcfg_device(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) + +int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg); + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index cf6abce..a5de684 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -8,10 +8,13 @@ extern struct hda_codec_preset snd_hda_preset_realtek[]; extern struct hda_codec_preset snd_hda_preset_cmedia[]; /* Analog Devices codecs */ extern struct hda_codec_preset snd_hda_preset_analog[]; +/* SigmaTel codecs */ +extern struct hda_codec_preset snd_hda_preset_sigmatel[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, snd_hda_preset_cmedia, snd_hda_preset_analog, + snd_hda_preset_sigmatel, NULL }; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 4d5db7fa..de1217b 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -68,21 +68,27 @@ static void print_amp_caps(snd_info_buffer_t *buffer, static void print_amp_vals(snd_info_buffer_t *buffer, struct hda_codec *codec, hda_nid_t nid, - int dir, int stereo) + int dir, int stereo, int indices) { unsigned int val; - if (stereo) { + int i; + + if (dir == HDA_OUTPUT) + dir = AC_AMP_GET_OUTPUT; + else + dir = AC_AMP_GET_INPUT; + for (i = 0; i < indices; i++) { + snd_iprintf(buffer, " ["); + if (stereo) { + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_LEFT | dir | i); + snd_iprintf(buffer, "0x%02x ", val); + } val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | - (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : - AC_AMP_GET_INPUT)); - snd_iprintf(buffer, "0x%02x ", val); + AC_AMP_GET_RIGHT | dir | i); + snd_iprintf(buffer, "0x%02x]", val); } - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | - (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : - AC_AMP_GET_INPUT)); - snd_iprintf(buffer, "0x%02x\n", val); + snd_iprintf(buffer, "\n"); } static void print_pcm_caps(snd_info_buffer_t *buffer, @@ -157,6 +163,7 @@ static const char *get_jack_color(u32 cfg) static void print_pin_caps(snd_info_buffer_t *buffer, struct hda_codec *codec, hda_nid_t nid) { + static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; static char *jack_types[16] = { "Line Out", "Speaker", "HP Out", "CD", "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", @@ -176,7 +183,8 @@ static void print_pin_caps(snd_info_buffer_t *buffer, snd_iprintf(buffer, " HP"); snd_iprintf(buffer, "\n"); caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); - snd_iprintf(buffer, " Pin Default 0x%08x: %s at %s %s\n", caps, + snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, + jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], get_jack_location(caps)); @@ -215,6 +223,9 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer) unsigned int wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + int conn_len = 0; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, get_wid_type_name(wid_type), wid_caps); if (wid_caps & AC_WCAP_STEREO) @@ -229,19 +240,23 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer) snd_iprintf(buffer, " Amp-Out"); snd_iprintf(buffer, "\n"); + if (wid_caps & AC_WCAP_CONN_LIST) + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + if (wid_caps & AC_WCAP_IN_AMP) { snd_iprintf(buffer, " Amp-In caps: "); print_amp_caps(buffer, codec, nid, HDA_INPUT); snd_iprintf(buffer, " Amp-In vals: "); print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO); + wid_caps & AC_WCAP_STEREO, conn_len); } if (wid_caps & AC_WCAP_OUT_AMP) { snd_iprintf(buffer, " Amp-Out caps: "); print_amp_caps(buffer, codec, nid, HDA_OUTPUT); snd_iprintf(buffer, " Amp-Out vals: "); print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO); + wid_caps & AC_WCAP_STEREO, 1); } if (wid_type == AC_WID_PIN) { @@ -265,14 +280,17 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer) } if (wid_caps & AC_WCAP_CONN_LIST) { - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - int c, conn_len; - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); + int c, curr = -1; + if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); snd_iprintf(buffer, " Connection: %d\n", conn_len); snd_iprintf(buffer, " "); - for (c = 0; c < conn_len; c++) + for (c = 0; c < conn_len; c++) { snd_iprintf(buffer, " 0x%02x", conn[c]); + if (c == curr) + snd_iprintf(buffer, "*"); + } snd_iprintf(buffer, "\n"); } } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 75d2384..2fd05bb 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1,5 +1,5 @@ /* - * HD audio interface patch for AD1986A + * HD audio interface patch for AD1981HD, AD1983, AD1986A * * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de> * @@ -27,13 +27,239 @@ #include "hda_codec.h" #include "hda_local.h" -struct ad1986a_spec { +struct ad198x_spec { struct semaphore amp_mutex; /* PCM volume/mute control mutex */ struct hda_multi_out multiout; /* playback */ + hda_nid_t adc_nid; + const struct hda_input_mux *input_mux; unsigned int cur_mux; /* capture source */ + unsigned int spdif_route; + snd_kcontrol_new_t *mixers; + const struct hda_verb *init_verbs; struct hda_pcm pcm_rec[2]; /* PCM information */ }; +/* + * input MUX handling (common part) + */ +static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} + +static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_mux; + return 0; +} + +static int ad198x_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + spec->adc_nid, &spec->cur_mux); +} + +/* + * initialization (common callbacks) + */ +static int ad198x_init(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_sequence_write(codec, spec->init_verbs); + return 0; +} + +static int ad198x_build_controls(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + int err; + + err = snd_hda_add_new_ctls(codec, spec->mixers); + if (err < 0) + return err; + if (spec->multiout.dig_out_nid) + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + return 0; +} + +/* + * Analog playback callbacks + */ +static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->adc_nid, stream_tag, 0, format); + return 0; +} + +static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->adc_nid, 0, 0, 0); + return 0; +} + + +/* + */ +static struct hda_pcm_stream ad198x_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 6, + .nid = 0, /* fill later */ + .ops = { + .open = ad198x_playback_pcm_open, + .prepare = ad198x_playback_pcm_prepare, + .cleanup = ad198x_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ad198x_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = ad198x_capture_pcm_prepare, + .cleanup = ad198x_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ad198x_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .open = ad198x_dig_playback_pcm_open, + .close = ad198x_dig_playback_pcm_close + }, +}; + +static int ad198x_build_pcms(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "AD198x Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nid; + + if (spec->multiout.dig_out_nid) { + info++; + codec->num_pcms++; + info->name = "AD198x Digital"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + + return 0; +} + +static void ad198x_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +#ifdef CONFIG_PM +static int ad198x_resume(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + ad198x_init(codec); + snd_hda_resume_ctls(codec, spec->mixers); + snd_hda_resume_spdif_out(codec); + return 0; +} +#endif + +static struct hda_codec_ops ad198x_patch_ops = { + .build_controls = ad198x_build_controls, + .build_pcms = ad198x_build_pcms, + .init = ad198x_init, + .free = ad198x_free, +#ifdef CONFIG_PM + .resume = ad198x_resume, +#endif +}; + + +/* + * AD1986A specific + */ + #define AD1986A_SPDIF_OUT 0x02 #define AD1986A_FRONT_DAC 0x03 #define AD1986A_SURR_DAC 0x04 @@ -68,7 +294,7 @@ static struct hda_input_mux ad1986a_capture_source = { static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *ad = codec->spec; + struct ad198x_spec *ad = codec->spec; down(&ad->amp_mutex); snd_hda_mixer_amp_volume_get(kcontrol, ucontrol); @@ -79,7 +305,7 @@ static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *ad = codec->spec; + struct ad198x_spec *ad = codec->spec; int i, change = 0; down(&ad->amp_mutex); @@ -92,12 +318,12 @@ static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ return change; } -#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_volume_info +#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *ad = codec->spec; + struct ad198x_spec *ad = codec->spec; down(&ad->amp_mutex); snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); @@ -108,7 +334,7 @@ static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *ad = codec->spec; + struct ad198x_spec *ad = codec->spec; int i, change = 0; down(&ad->amp_mutex); @@ -122,32 +348,6 @@ static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t } /* - * input MUX handling - */ -static int ad1986a_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) -{ - return snd_hda_input_mux_info(&ad1986a_capture_source, uinfo); -} - -static int ad1986a_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_mux; - return 0; -} - -static int ad1986a_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad1986a_spec *spec = codec->spec; - - return snd_hda_input_mux_put(codec, &ad1986a_capture_source, ucontrol, - AD1986A_ADC, &spec->cur_mux); -} - -/* * mixers */ static snd_kcontrol_new_t ad1986a_mixers[] = { @@ -194,9 +394,9 @@ static snd_kcontrol_new_t ad1986a_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", - .info = ad1986a_mux_enum_info, - .get = ad1986a_mux_enum_get, - .put = ad1986a_mux_enum_put, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, }, HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT), { } /* end */ @@ -241,183 +441,328 @@ static struct hda_verb ad1986a_init_verbs[] = { {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* HP Pin */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* Front, Surround, CLFE Pins */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* Mono Pin */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* Mic Pin */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* Line, Aux, CD, Beep-In Pin */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, { } /* end */ }; -static int ad1986a_init(struct hda_codec *codec) +static int patch_ad1986a(struct hda_codec *codec) { - snd_hda_sequence_write(codec, ad1986a_init_verbs); - return 0; -} + struct ad198x_spec *spec; -static int ad1986a_build_controls(struct hda_codec *codec) -{ - int err; + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + init_MUTEX(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 6; + spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); + spec->multiout.dac_nids = ad1986a_dac_nids; + spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT; + spec->adc_nid = AD1986A_ADC; + spec->input_mux = &ad1986a_capture_source; + spec->mixers = ad1986a_mixers; + spec->init_verbs = ad1986a_init_verbs; + + codec->patch_ops = ad198x_patch_ops; - err = snd_hda_add_new_ctls(codec, ad1986a_mixers); - if (err < 0) - return err; - err = snd_hda_create_spdif_out_ctls(codec, AD1986A_SPDIF_OUT); - if (err < 0) - return err; return 0; } /* - * Analog playback callbacks + * AD1983 specific */ -static int ad1986a_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) -{ - struct ad1986a_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); -} -static int ad1986a_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - snd_pcm_substream_t *substream) -{ - struct ad1986a_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} +#define AD1983_SPDIF_OUT 0x02 +#define AD1983_DAC 0x03 +#define AD1983_ADC 0x04 -static int ad1986a_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) -{ - struct ad1986a_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} +static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC }; + +static struct hda_input_mux ad1983_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x1 }, + { "Mix", 0x2 }, + { "Mix Mono", 0x3 }, + }, +}; /* - * Digital out + * SPDIF playback route */ -static int ad1986a_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) +static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { - struct ad1986a_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); + static char *texts[] = { "PCM", "ADC" }; + + 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 ad1986a_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) +static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - struct ad1986a_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; -/* - * Analog capture - */ -static int ad1986a_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - snd_pcm_substream_t *substream) -{ - snd_hda_codec_setup_stream(codec, AD1986A_ADC, stream_tag, 0, format); + ucontrol->value.enumerated.item[0] = spec->spdif_route; return 0; } -static int ad1986a_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) +static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - snd_hda_codec_setup_stream(codec, AD1986A_ADC, 0, 0, 0); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { + spec->spdif_route = ucontrol->value.enumerated.item[0]; + snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0, + AC_VERB_SET_CONNECT_SEL, spec->spdif_route); + return 1; + } return 0; } - -/* - */ -static struct hda_pcm_stream ad1986a_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, - .nid = AD1986A_FRONT_DAC, /* NID to query formats and rates */ - .ops = { - .open = ad1986a_playback_pcm_open, - .prepare = ad1986a_playback_pcm_prepare, - .cleanup = ad1986a_playback_pcm_cleanup +static snd_kcontrol_new_t ad1983_mixers[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, }, -}; - -static struct hda_pcm_stream ad1986a_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = AD1986A_ADC, /* NID to query formats and rates */ - .ops = { - .prepare = ad1986a_capture_pcm_prepare, - .cleanup = ad1986a_capture_pcm_cleanup + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Route", + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, }, + { } /* end */ }; -static struct hda_pcm_stream ad1986a_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = AD1986A_SPDIF_OUT, - .ops = { - .open = ad1986a_dig_playback_pcm_open, - .close = ad1986a_dig_playback_pcm_close - }, +static struct hda_verb ad1983_init_verbs[] = { + /* Front, HP, Mono; mute as default */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Beep, PCM, Mic, Line-In: mute */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Front, HP selectors; from Mix */ + {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* Mono selector; from Mix */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic selector; Mic */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Line-in selector: Line-in */ + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Mic boost: 0dB */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* Record selector: mic */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* SPDIF route: PCM */ + {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Front Pin */ + {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* HP Pin */ + {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* Mono Pin */ + {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* Mic Pin */ + {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* Line Pin */ + {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + { } /* end */ }; -static int ad1986a_build_pcms(struct hda_codec *codec) +static int patch_ad1983(struct hda_codec *codec) { - struct ad1986a_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; + struct ad198x_spec *spec; - codec->num_pcms = 2; - codec->pcm_info = info; + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; - info->name = "AD1986A Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1986a_pcm_analog_capture; - info++; + init_MUTEX(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); + spec->multiout.dac_nids = ad1983_dac_nids; + spec->multiout.dig_out_nid = AD1983_SPDIF_OUT; + spec->adc_nid = AD1983_ADC; + spec->input_mux = &ad1983_capture_source; + spec->mixers = ad1983_mixers; + spec->init_verbs = ad1983_init_verbs; + spec->spdif_route = 0; - info->name = "AD1986A Digital"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_digital_playback; + codec->patch_ops = ad198x_patch_ops; return 0; } -static void ad1986a_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} -#ifdef CONFIG_PM -static int ad1986a_resume(struct hda_codec *codec) -{ - ad1986a_init(codec); - snd_hda_resume_ctls(codec, ad1986a_mixers); - snd_hda_resume_spdif_out(codec); - return 0; -} -#endif +/* + * AD1981 HD specific + */ -static struct hda_codec_ops ad1986a_patch_ops = { - .build_controls = ad1986a_build_controls, - .build_pcms = ad1986a_build_pcms, - .init = ad1986a_init, - .free = ad1986a_free, -#ifdef CONFIG_PM - .resume = ad1986a_resume, -#endif +#define AD1981_SPDIF_OUT 0x02 +#define AD1981_DAC 0x03 +#define AD1981_ADC 0x04 + +static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC }; + +/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */ +static struct hda_input_mux ad1981_capture_source = { + .num_items = 7, + .items = { + { "Front Mic", 0x0 }, + { "Line", 0x1 }, + { "Mix", 0x2 }, + { "Mix Mono", 0x3 }, + { "CD", 0x4 }, + { "Mic", 0x6 }, + { "Aux", 0x7 }, + }, }; -static int patch_ad1986a(struct hda_codec *codec) +static snd_kcontrol_new_t ad1981_mixers[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* identical with AD1983 */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Route", + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +static struct hda_verb ad1981_init_verbs[] = { + /* Front, HP, Mono; mute as default */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Front, HP selectors; from Mix */ + {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* Mono selector; from Mix */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic Mixer; select Front Mic */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Mic boost: 0dB */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* Record selector: Front mic */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* SPDIF route: PCM */ + {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Front Pin */ + {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* HP Pin */ + {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* Mono Pin */ + {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* Front & Rear Mic Pins */ + {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* Line Pin */ + {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* Digital Beep */ + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Line-Out as Input: disabled */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + { } /* end */ +}; + +static int patch_ad1981(struct hda_codec *codec) { - struct ad1986a_spec *spec; + struct ad198x_spec *spec; spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -426,20 +771,28 @@ static int patch_ad1986a(struct hda_codec *codec) init_MUTEX(&spec->amp_mutex); codec->spec = spec; - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); - spec->multiout.dac_nids = ad1986a_dac_nids; - spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); + spec->multiout.dac_nids = ad1981_dac_nids; + spec->multiout.dig_out_nid = AD1981_SPDIF_OUT; + spec->adc_nid = AD1981_ADC; + spec->input_mux = &ad1981_capture_source; + spec->mixers = ad1981_mixers; + spec->init_verbs = ad1981_init_verbs; + spec->spdif_route = 0; - codec->patch_ops = ad1986a_patch_ops; + codec->patch_ops = ad198x_patch_ops; return 0; } + /* * patch entries */ struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, + { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index b7cc8e4..2d6e3e3 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -29,6 +29,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#define NUM_PINS 11 /* board config type */ @@ -38,6 +39,7 @@ enum { CMI_FULL, /* back 6-jack + front-panel 2-jack */ CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */ CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */ + CMI_AUTO, /* let driver guess it */ }; struct cmi_spec { @@ -48,6 +50,8 @@ struct cmi_spec { /* playback */ struct hda_multi_out multiout; + hda_nid_t dac_nids[4]; /* NID for each DAC */ + int num_dacs; /* capture */ hda_nid_t *adc_nids; @@ -63,8 +67,30 @@ struct cmi_spec { const struct cmi_channel_mode *channel_modes; struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* pin deafault configuration */ + hda_nid_t pin_nid[NUM_PINS]; + unsigned int def_conf[NUM_PINS]; + unsigned int pin_def_confs; + + /* multichannel pins */ + hda_nid_t multich_pin[4]; /* max 8-channel */ + struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */ }; +/* amp values */ +#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) +#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define AMP_OUT_ZERO 0xb000 +/* pinctl values */ +#define PIN_IN 0x20 +#define PIN_VREF80 0x24 +#define PIN_VREF50 0x21 +#define PIN_OUT 0x40 +#define PIN_HP 0xc0 + /* * input MUX */ @@ -102,9 +128,9 @@ static int cmi_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon /* 3-stack / 2 channel */ static struct hda_verb cmi9880_ch2_init[] = { /* set line-in PIN for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* set mic PIN for input, also enable vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* route front PCM (DAC1) to HP */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, {} @@ -113,9 +139,9 @@ static struct hda_verb cmi9880_ch2_init[] = { /* 3-stack / 6 channel */ static struct hda_verb cmi9880_ch6_init[] = { /* set line-in PIN for output */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* set mic PIN for output */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* route front PCM (DAC1) to HP */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, {} @@ -124,9 +150,9 @@ static struct hda_verb cmi9880_ch6_init[] = { /* 3-stack+front / 8 channel */ static struct hda_verb cmi9880_ch8_init[] = { /* set line-in PIN for output */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* set mic PIN for output */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* route rear-surround PCM (DAC4) to HP */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 }, {} @@ -269,25 +295,27 @@ static hda_nid_t cmi9880_adc_nids[2] = { */ static struct hda_verb cmi9880_basic_init[] = { /* port-D for line out (rear panel) */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* route front PCM to HP */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 }, /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 }, /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* route front mic to ADC1/2 */ { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, @@ -296,23 +324,27 @@ static struct hda_verb cmi9880_basic_init[] = { static struct hda_verb cmi9880_allout_init[] = { /* port-D for line out (rear panel) */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* route front PCM to HP */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-A for side (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 }, /* port-C for surround (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* route front mic to ADC1/2 */ { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 }, { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 }, @@ -347,6 +379,81 @@ static int cmi9880_build_controls(struct hda_codec *codec) return 0; } +/* fill in the multi_dac_nids table, which will decide + which audio widget to use for each channel */ +static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid; + int assigned[4]; + int i, j; + + /* clear the table, only one c-media dac assumed here */ + memset(spec->dac_nids, 0, sizeof(spec->dac_nids)); + memset(assigned, 0, sizeof(assigned)); + /* check the pins we found */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ + if (nid >= 0x0b && nid <= 0x0e) { + spec->dac_nids[i] = (nid - 0x0b) + 0x03; + assigned[nid - 0x0b] = 1; + } + } + /* left pin can be connect to any audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (nid <= 0x0e) + continue; + /* search for an empty channel */ + for (j = 0; j < cfg->line_outs; j++) { + if (! assigned[j]) { + spec->dac_nids[i] = i + 0x03; + assigned[j] = 1; + break; + } + } + } + spec->num_dacs = cfg->line_outs; + return 0; +} + +/* create multi_init table, which is used for multichannel initialization */ +static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid; + int i, j, k, len; + + /* clear the table, only one c-media dac assumed here */ + memset(spec->multi_init, 0, sizeof(spec->multi_init)); + for (j = 0, i = 0; i < cfg->line_outs; i++) { + hda_nid_t conn[4]; + nid = cfg->line_out_pins[i]; + /* set as output */ + spec->multi_init[j].nid = nid; + spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL; + spec->multi_init[j].param = PIN_OUT; + j++; + if (nid > 0x0e) { + /* set connection */ + spec->multi_init[j].nid = nid; + spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; + spec->multi_init[j].param = 0; + /* find the index in connect list */ + len = snd_hda_get_connections(codec, nid, conn, 4); + for (k = 0; k < len; k++) + if (conn[k] == spec->dac_nids[i]) { + spec->multi_init[j].param = j; + break; + } + j++; + break; + } + } + return 0; +} + static int cmi9880_init(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; @@ -354,6 +461,8 @@ static int cmi9880_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cmi9880_allout_init); else snd_hda_sequence_write(codec, cmi9880_basic_init); + if (spec->board_config == CMI_AUTO) + snd_hda_sequence_write(codec, spec->multi_init); return 0; } @@ -540,6 +649,7 @@ static struct hda_board_config cmi9880_cfg_tbl[] = { { .modelname = "full", .config = CMI_FULL }, { .modelname = "full_dig", .config = CMI_FULL_DIG }, { .modelname = "allout", .config = CMI_ALLOUT }, + { .modelname = "auto", .config = CMI_AUTO }, {} /* terminator */ }; @@ -564,10 +674,14 @@ static int patch_cmi9880(struct hda_codec *codec) codec->spec = spec; spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl); if (spec->board_config < 0) { - snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); - spec->board_config = CMI_FULL_DIG; /* try everything */ + snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); + spec->board_config = CMI_AUTO; /* try everything */ } + /* copy default DAC NIDs */ + memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); + spec->num_dacs = 4; + switch (spec->board_config) { case CMI_MINIMAL: case CMI_MIN_FP: @@ -599,10 +713,58 @@ static int patch_cmi9880(struct hda_codec *codec) spec->input_mux = &cmi9880_no_line_mux; spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; break; + case CMI_AUTO: + { + unsigned int port_e, port_f, port_g, port_h; + unsigned int port_spdifi, port_spdifo; + struct auto_pin_cfg cfg; + + /* collect pin default configuration */ + port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + spec->front_panel = 1; + if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || + get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { + port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + spec->surr_switch = 1; + /* no front panel */ + if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || + get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) { + /* no optional rear panel */ + spec->board_config = CMI_MINIMAL; + spec->front_panel = 0; + spec->num_ch_modes = 2; + } else { + spec->board_config = CMI_MIN_FP; + spec->num_ch_modes = 3; + } + spec->channel_modes = cmi9880_channel_modes; + spec->input_mux = &cmi9880_basic_mux; + spec->multiout.max_channels = cmi9880_channel_modes[0].channels; + } else { + spec->input_mux = &cmi9880_basic_mux; + port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) + spec->dig_in_nid = CMI_DIG_IN_NID; + spec->multiout.max_channels = 8; + } + snd_hda_parse_pin_def_config(codec, &cfg); + if (cfg.line_outs) { + spec->multiout.max_channels = cfg.line_outs * 2; + cmi9880_fill_multi_dac_nids(codec, &cfg); + cmi9880_fill_multi_init(codec, &cfg); + } else + snd_printd("patch_cmedia: cannot detect association in defcfg\n"); + break; + } } - spec->multiout.num_dacs = 4; - spec->multiout.dac_nids = cmi9880_dac_nids; + spec->multiout.num_dacs = spec->num_dacs; + spec->multiout.dac_nids = spec->dac_nids; spec->adc_nids = cmi9880_adc_nids; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 17c5062..bab8984 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -33,38 +33,73 @@ /* ALC880 board config type */ enum { - ALC880_MINIMAL, ALC880_3ST, ALC880_3ST_DIG, ALC880_5ST, ALC880_5ST_DIG, ALC880_W810, + ALC880_Z71V, + ALC880_AUTO, + ALC880_6ST_DIG, + ALC880_F1734, + ALC880_ASUS, + ALC880_ASUS_DIG, + ALC880_ASUS_W1V, + ALC880_UNIWILL_DIG, +#ifdef CONFIG_SND_DEBUG + ALC880_TEST, +#endif + ALC880_MODEL_LAST /* last tag */ +}; + +/* ALC260 models */ +enum { + ALC260_BASIC, + ALC260_HP, + ALC260_MODEL_LAST /* last tag */ }; +/* amp values */ +#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) +#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define AMP_OUT_ZERO 0xb000 +/* pinctl values */ +#define PIN_IN 0x20 +#define PIN_VREF80 0x24 +#define PIN_VREF50 0x21 +#define PIN_OUT 0x40 +#define PIN_HP 0xc0 + struct alc_spec { /* codec parameterization */ - unsigned int front_panel: 1; - - snd_kcontrol_new_t* mixers[2]; + snd_kcontrol_new_t *mixers[3]; /* mixer arrays */ unsigned int num_mixers; - struct hda_verb *init_verbs; + const struct hda_verb *init_verbs[3]; /* initialization verbs + * don't forget NULL termination! + */ + unsigned int num_init_verbs; - char* stream_name_analog; + char *stream_name_analog; /* analog PCM stream */ struct hda_pcm_stream *stream_analog_playback; struct hda_pcm_stream *stream_analog_capture; - char* stream_name_digital; + char *stream_name_digital; /* digital PCM stream */ struct hda_pcm_stream *stream_digital_playback; struct hda_pcm_stream *stream_digital_capture; /* playback */ - struct hda_multi_out multiout; + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ /* capture source */ const struct hda_input_mux *input_mux; @@ -75,61 +110,17 @@ struct alc_spec { int num_channel_mode; /* PCM information */ - struct hda_pcm pcm_rec[2]; -}; - -/* DAC/ADC assignment */ - -static hda_nid_t alc880_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x05, 0x04, 0x03 -}; - -static hda_nid_t alc880_w810_dac_nids[3] = { - /* front, rear/surround, clfe */ - 0x02, 0x03, 0x04 -}; - -static hda_nid_t alc880_adc_nids[3] = { - /* ADC0-2 */ - 0x07, 0x08, 0x09, -}; - -#define ALC880_DIGOUT_NID 0x06 -#define ALC880_DIGIN_NID 0x0a + struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ -static hda_nid_t alc260_dac_nids[1] = { - /* front */ - 0x02, -}; + struct semaphore bind_mutex; /* for bound controls */ -static hda_nid_t alc260_adc_nids[2] = { - /* ADC0-1 */ - 0x04, 0x05, + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + unsigned int num_kctl_alloc, num_kctl_used; + snd_kcontrol_new_t *kctl_alloc; + struct hda_input_mux private_imux; }; -#define ALC260_DIGOUT_NID 0x03 -#define ALC260_DIGIN_NID 0x06 - -static struct hda_input_mux alc880_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x3 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static struct hda_input_mux alc260_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; /* * input MUX handling @@ -160,6 +151,7 @@ static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); } + /* * channel mode setting */ @@ -168,135 +160,18 @@ struct alc_channel_mode { const struct hda_verb *sequence; }; - -/* - * channel source setting (2/6 channel selection for 3-stack) - */ - -/* - * set the path ways for 2 channel output - * need to set the codec line out and mic 1 pin widgets to inputs - */ -static struct hda_verb alc880_threestack_ch2_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* set pin widget 18h (mic1) for input, for mic also enable the vref */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* mute the output for Line In PW */ - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - /* mute for Mic1 PW */ - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { } /* end */ -}; - -/* - * 6ch mode - * need to set the codec line out and mic 1 pin widgets to outputs - */ -static struct hda_verb alc880_threestack_ch6_init[] = { - /* set pin widget 1Ah (line in) for output */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* set pin widget 18h (mic1) for output */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* unmute the output for Line In PW */ - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - /* unmute for Mic1 PW */ - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - /* for rear channel output using Line In 1 - * set select widget connection (nid = 0x12) - to summer node - * for rear NID = 0x0f...offset 3 in connection list - */ - { 0x12, AC_VERB_SET_CONNECT_SEL, 0x3 }, - /* for Mic1 - retask for center/lfe */ - /* set select widget connection (nid = 0x10) - to summer node for - * front CLFE NID = 0x0e...offset 2 in connection list - */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x2 }, - { } /* end */ -}; - -static struct alc_channel_mode alc880_threestack_modes[2] = { - { 2, alc880_threestack_ch2_init }, - { 6, alc880_threestack_ch6_init }, -}; - - -/* - * channel source setting (6/8 channel selection for 5-stack) - */ - -/* set the path ways for 6 channel output - * need to set the codec line out and mic 1 pin widgets to inputs - */ -static struct hda_verb alc880_fivestack_ch6_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* mute the output for Line In PW */ - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { } /* end */ -}; - -/* need to set the codec line out and mic 1 pin widgets to outputs */ -static struct hda_verb alc880_fivestack_ch8_init[] = { - /* set pin widget 1Ah (line in) for output */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* unmute the output for Line In PW */ - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - /* output for surround channel output using Line In 1 */ - /* set select widget connection (nid = 0x12) - to summer node - * for surr_rear NID = 0x0d...offset 1 in connection list - */ - { 0x12, AC_VERB_SET_CONNECT_SEL, 0x1 }, - { } /* end */ -}; - -static struct alc_channel_mode alc880_fivestack_modes[2] = { - { 6, alc880_fivestack_ch6_init }, - { 8, alc880_fivestack_ch8_init }, -}; - -/* - * channel source setting for W810 system - * - * W810 has rear IO for: - * Front (DAC 02) - * Surround (DAC 03) - * Center/LFE (DAC 04) - * Digital out (06) - * - * The system also has a pair of internal speakers, and a headphone jack. - * These are both connected to Line2 on the codec, hence to DAC 02. - * - * There is a variable resistor to control the speaker or headphone - * volume. This is a hardware-only device without a software API. - * - * Plugging headphones in will disable the internal speakers. This is - * implemented in hardware, not via the driver using jack sense. In - * a similar fashion, plugging into the rear socket marked "front" will - * disable both the speakers and headphones. - * - * For input, there's a microphone jack, and an "audio in" jack. - * These may not do anything useful with this driver yet, because I - * haven't setup any initialization verbs for these yet... - */ - -static struct alc_channel_mode alc880_w810_modes[1] = { - { 6, NULL } -}; - -/* - */ static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + int items = kcontrol->private_value ? (int)kcontrol->private_value : 2; snd_assert(spec->channel_mode, return -ENXIO); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; sprintf(uinfo->value.enumerated.name, "%dch", spec->channel_mode[uinfo->value.enumerated.item].channels); return 0; @@ -306,10 +181,16 @@ static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + int items = kcontrol->private_value ? (int)kcontrol->private_value : 2; + int i; snd_assert(spec->channel_mode, return -ENXIO); - ucontrol->value.enumerated.item[0] = - (spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1; + for (i = 0; i < items; i++) { + if (spec->multiout.max_channels == spec->channel_mode[i].channels) { + ucontrol->value.enumerated.item[0] = i; + break; + } + } return 0; } @@ -335,21 +216,149 @@ static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc /* + * bound volume controls + * + * bind multiple volumes (# indices, from 0) + */ + +#define AMP_VAL_IDX_SHIFT 19 +#define AMP_VAL_IDX_MASK (0x0f<<19) + +static int alc_bind_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned long pval; + + down(&spec->bind_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + snd_hda_mixer_amp_switch_info(kcontrol, uinfo); + kcontrol->private_value = pval; + up(&spec->bind_mutex); + return 0; +} + +static int alc_bind_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned long pval; + + down(&spec->bind_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); + kcontrol->private_value = pval; + up(&spec->bind_mutex); + return 0; +} + +static int alc_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned long pval; + int i, indices, change = 0; + + down(&spec->bind_mutex); + pval = kcontrol->private_value; + indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; + for (i = 0; i < indices; i++) { + kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | (i << AMP_VAL_IDX_SHIFT); + change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + } + kcontrol->private_value = pval; + up(&spec->bind_mutex); + return change; +} + +#define ALC_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = alc_bind_switch_info, \ + .get = alc_bind_switch_get, \ + .put = alc_bind_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) } + +#define ALC_BIND_MUTE(xname,nid,indices,dir) ALC_BIND_MUTE_MONO(xname,nid,3,indices,dir) + + +/* + * ALC880 3-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) + * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b + * HP = 0x19 */ -/* 3-stack mode - * Pin assignment: Front=0x14, Line-In/Rear=0x1a, Mic/CLFE=0x18, F-Mic=0x1b - * HP=0x19 +static hda_nid_t alc880_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x05, 0x04, 0x03 +}; + +static hda_nid_t alc880_adc_nids[3] = { + /* ADC0-2 */ + 0x07, 0x08, 0x09, +}; + +/* The datasheet says the node 0x07 is connected from inputs, + * but it shows zero connection in the real implementation on some devices. */ -static snd_kcontrol_new_t alc880_base_mixer[] = { +static hda_nid_t alc880_adc_nids_alt[2] = { + /* ADC1-2 */ + 0x08, 0x09, +}; + +#define ALC880_DIGOUT_NID 0x06 +#define ALC880_DIGIN_NID 0x0a + +static struct hda_input_mux alc880_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* channel source setting (2/6 channel selection for 3-stack) */ +/* 2ch mode */ +static struct hda_verb alc880_threestack_ch2_init[] = { + /* set line-in to input, mute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + /* set mic-in to input vref 80%, mute it */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* 6ch mode */ +static struct hda_verb alc880_threestack_ch6_init[] = { + /* set line-in to output, unmute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + /* set mic-in to output, unmute it */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static struct alc_channel_mode alc880_threestack_modes[2] = { + { 2, alc880_threestack_ch2_init }, + { 6, alc880_threestack_ch6_init }, +}; + +static snd_kcontrol_new_t alc880_three_stack_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x18, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x18, 2, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), @@ -360,12 +369,25 @@ static snd_kcontrol_new_t alc880_base_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc880_ch_mode_info, + .get = alc880_ch_mode_get, + .put = alc880_ch_mode_put, + }, + { } /* end */ +}; + +/* capture mixer elements */ +static snd_kcontrol_new_t alc880_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer @@ -374,65 +396,125 @@ static snd_kcontrol_new_t alc880_base_mixer[] = { */ /* .name = "Capture Source", */ .name = "Input Source", - .count = 2, + .count = 3, .info = alc_mux_enum_info, .get = alc_mux_enum_get, .put = alc_mux_enum_put, }, + { } /* end */ +}; + +/* capture mixer elements (in case NID 0x07 not available) */ +static snd_kcontrol_new_t alc880_capture_alt_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc880_ch_mode_info, - .get = alc880_ch_mode_get, - .put = alc880_ch_mode_put, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, }, { } /* end */ }; -/* 5-stack mode - * Pin assignment: Front=0x14, Rear=0x17, CLFE=0x16 - * Line-In/Side=0x1a, Mic=0x18, F-Mic=0x1b, HP=0x19 + + +/* + * ALC880 5-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd) + * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 + * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 */ + +/* additional mixers to alc880_three_stack_mixer */ static snd_kcontrol_new_t alc880_five_stack_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT), + { } /* end */ +}; + +/* channel source setting (6/8 channel selection for 5-stack) */ +/* 6ch mode */ +static struct hda_verb alc880_fivestack_ch6_init[] = { + /* set line-in to input, mute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* 8ch mode */ +static struct hda_verb alc880_fivestack_ch8_init[] = { + /* set line-in to output, unmute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static struct alc_channel_mode alc880_fivestack_modes[2] = { + { 6, alc880_fivestack_ch6_init }, + { 8, alc880_fivestack_ch8_init }, +}; + + +/* + * ALC880 6-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f) + * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, + * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b + */ + +static hda_nid_t alc880_6st_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x03, 0x04, 0x05 +}; + +static struct hda_input_mux alc880_6stack_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* fixed 8-channels */ +static struct alc_channel_mode alc880_sixstack_modes[1] = { + { 8, NULL }, +}; + +static snd_kcontrol_new_t alc880_six_stack_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x17, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channel Mode", @@ -443,39 +525,169 @@ static snd_kcontrol_new_t alc880_five_stack_mixer[] = { { } /* end */ }; + +/* + * ALC880 W810 model + * + * W810 has rear IO for: + * Front (DAC 02) + * Surround (DAC 03) + * Center/LFE (DAC 04) + * Digital out (06) + * + * The system also has a pair of internal speakers, and a headphone jack. + * These are both connected to Line2 on the codec, hence to DAC 02. + * + * There is a variable resistor to control the speaker or headphone + * volume. This is a hardware-only device without a software API. + * + * Plugging headphones in will disable the internal speakers. This is + * implemented in hardware, not via the driver using jack sense. In + * a similar fashion, plugging into the rear socket marked "front" will + * disable both the speakers and headphones. + * + * For input, there's a microphone jack, and an "audio in" jack. + * These may not do anything useful with this driver yet, because I + * haven't setup any initialization verbs for these yet... + */ + +static hda_nid_t alc880_w810_dac_nids[3] = { + /* front, rear/surround, clfe */ + 0x02, 0x03, 0x04 +}; + +/* fixed 6 channels */ +static struct alc_channel_mode alc880_w810_modes[1] = { + { 6, NULL } +}; + +/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */ static snd_kcontrol_new_t alc880_w810_base_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), + { } /* end */ +}; + + +/* + * Z710V model + * + * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) + * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a + */ + +static hda_nid_t alc880_z71v_dac_nids[1] = { + 0x02 +}; +#define ALC880_Z71V_HP_DAC 0x03 + +/* fixed 2 channels */ +static struct alc_channel_mode alc880_2_jack_modes[1] = { + { 2, NULL } +}; + +static snd_kcontrol_new_t alc880_z71v_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +/* FIXME! */ +/* + * ALC880 F1734 model + * + * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d) + * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18 + */ + +static hda_nid_t alc880_f1734_dac_nids[1] = { + 0x03 +}; +#define ALC880_F1734_HP_DAC 0x02 + +static snd_kcontrol_new_t alc880_f1734_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +/* FIXME! */ +/* + * ALC880 ASUS model + * + * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) + * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, + * Mic = 0x18, Line = 0x1a + */ + +#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */ +#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */ + +static snd_kcontrol_new_t alc880_asus_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, + .name = "Channel Mode", + .info = alc880_ch_mode_info, + .get = alc880_ch_mode_get, + .put = alc880_ch_mode_put, }, { } /* end */ }; +/* FIXME! */ /* + * ALC880 ASUS W1V model + * + * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) + * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, + * Mic = 0x18, Line = 0x1a, Line2 = 0x1b + */ + +/* additional mixers to alc880_asus_mixer */ +static snd_kcontrol_new_t alc880_asus_w1v_mixer[] = { + HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT), + { } /* end */ +}; + + +/* + * build control elements */ static int alc_build_controls(struct hda_codec *codec) { @@ -502,227 +714,297 @@ static int alc_build_controls(struct hda_codec *codec) return 0; } + /* * initialize the codec volumes, etc */ -static struct hda_verb alc880_init_verbs_three_stack[] = { - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* unmute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute front mixer amp left (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute rear mixer amp left and right (volume = 0) */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute rear mixer amp left and right (volume = 0) */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - /* using rear surround as the path for headphone output */ - /* unmute rear surround mixer amp left and right (volume = 0) */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* PASD 3 stack boards use the Mic 2 as the headphone output */ - /* need to program the selector associated with the Mic 2 pin widget to - * surround path (index 0x01) for headphone output */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* need to retask the Mic 2 pin widget to output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer widget(nid=0x0B) - * to support the input path of analog loopback +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc880_volume_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for front panel * mic (mic 2) */ - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */ - /* unmute CD */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* unmute Line In */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - /* unmute Mic 1 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - /* unmute Line In 2 (for PASD boards Mic 2) */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - - /* Unmute input amps for the line out paths to support the output path of - * analog loopback - * the mixers on the output path has 2 inputs, one from the DAC and one - * from the mixer + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x0c - 0x0f) */ - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* Unmute Front out path */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Surround (used as HP) out path */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute C/LFE out path */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */ - /* Unmute rear Surround out path */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, { } }; -static struct hda_verb alc880_init_verbs_five_stack[] = { - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* unmute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute front mixer amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* five rear and clfe */ - /* unmute rear mixer amp left and right (volume = 0) */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute clfe mixer amp left and right (volume = 0) */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - /* using rear surround as the path for headphone output */ - /* unmute rear surround mixer amp left and right (volume = 0) */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* PASD 3 stack boards use the Mic 2 as the headphone output */ - /* need to program the selector associated with the Mic 2 pin widget to - * surround path (index 0x01) for headphone output - */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* need to retask the Mic 2 pin widget to output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer - * widget(nid=0x0B) to support the input path of analog loopback +/* + * 3-stack pin configuration: + * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b + */ +static struct hda_verb alc880_pin_3stack_init_verbs[] = { + /* + * preset connection lists of input pins + * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround */ - /* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */ - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/ - /* unmute CD */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* unmute Line In */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - /* unmute Mic 1 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - /* unmute Line In 2 (for PASD boards Mic 2) */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - - /* Unmute input amps for the line out paths to support the output path of - * analog loopback - * the mixers on the output path has 2 inputs, one from the DAC and - * one from the mixer + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ + + /* + * Set pin mode and muting */ - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* Unmute Front out path */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Surround (used as HP) out path */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute C/LFE out path */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */ - /* Unmute rear Surround out path */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mic2 (as headphone out) for HP output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line2 (as front mic) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, { } }; -static struct hda_verb alc880_w810_init_verbs[] = { - /* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - - /* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, - - /* front channel selector/amp: output 0: unmuted, max volume */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - - /* front out pin: muted, (no volume selection) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - - /* front out pin: NOT headphone enable, out enable, vref disabled */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +/* + * 5-stack pin configuration: + * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19, + * line-in/side = 0x1a, f-mic = 0x1b + */ +static struct hda_verb alc880_pin_5stack_init_verbs[] = { + /* + * preset connection lists of input pins + * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround + */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */ + /* + * Set pin mode and muting + */ + /* set pin widgets 0x14-0x17 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* unmute pins for output (no gain on this amp) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* surround channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mic2 (as headphone out) for HP output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line2 (as front mic) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* surround channel selector/amp: input 1: capture mix: muted, (no volume selection) */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, + { } +}; - /* surround channel selector/amp: output 0: unmuted, max volume */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, +/* + * W810 pin configuration: + * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b + */ +static struct hda_verb alc880_pin_w810_init_verbs[] = { + /* hphone/speaker input selector: front DAC */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* surround out pin: muted, (no volume selection) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* surround out pin: NOT headphone enable, out enable, vref disabled */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; - /* c/lfe channel selector/amp: input 0: DAC: unmuted, (no volume selection) */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, +/* + * Z71V pin configuration: + * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?) + */ +static struct hda_verb alc880_pin_z71v_init_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* c/lfe channel selector/amp: input 1: capture mix: muted, (no volume selection) */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* c/lfe channel selector/amp: output 0: unmuted, max volume */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + { } +}; - /* c/lfe out pin: muted, (no volume selection) */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, +/* + * 6-stack pin configuration: + * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19, + * line = 0x1a, HP = 0x1b + */ +static struct hda_verb alc880_pin_6stack_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; - /* c/lfe out pin: NOT headphone enable, out enable, vref disabled */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +/* FIXME! */ +/* + * F1734 pin configuration: + * HP = 0x14, speaker-out = 0x15, mic = 0x18 + */ +static struct hda_verb alc880_pin_f1734_init_verbs[] = { + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; - /* hphone/speaker input selector: front DAC */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, +/* FIXME! */ +/* + * ASUS pin configuration: + * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a + */ +static struct hda_verb alc880_pin_asus_init_verbs[] = { + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; - /* hphone/speaker out pin: muted, (no volume selection) */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, +/* Enable GPIO mask and set output */ +static struct hda_verb alc880_gpio1_init_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, +}; - /* hphone/speaker out pin: NOT headphone enable, out enable, vref disabled */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +/* Enable GPIO mask and set output */ +static struct hda_verb alc880_gpio2_init_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, +}; - { } -}; +/* + */ static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - snd_hda_sequence_write(codec, spec->init_verbs); + unsigned int i; + + for (i = 0; i < spec->num_init_verbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); return 0; } @@ -736,9 +1018,8 @@ static int alc_resume(struct hda_codec *codec) int i; alc_init(codec); - for (i = 0; i < spec->num_mixers; i++) { + for (i = 0; i < spec->num_mixers; i++) snd_hda_resume_ctls(codec, spec->mixers[i]); - } if (spec->multiout.dig_out_nid) snd_hda_resume_spdif_out(codec); if (spec->dig_in_nid) @@ -830,7 +1111,7 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, - .nid = 0x02, /* NID to query formats and rates */ + /* NID is set in alc_build_pcms */ .ops = { .open = alc880_playback_pcm_open, .prepare = alc880_playback_pcm_prepare, @@ -842,7 +1123,7 @@ static struct hda_pcm_stream alc880_pcm_analog_capture = { .substreams = 2, .channels_min = 2, .channels_max = 2, - .nid = 0x07, /* NID to query formats and rates */ + /* NID is set in alc_build_pcms */ .ops = { .prepare = alc880_capture_pcm_prepare, .cleanup = alc880_capture_pcm_cleanup @@ -878,7 +1159,9 @@ static int alc_build_pcms(struct hda_codec *codec) info->name = spec->stream_name_analog; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; for (i = 0; i < spec->num_channel_mode; i++) { @@ -906,7 +1189,18 @@ static int alc_build_pcms(struct hda_codec *codec) static void alc_free(struct hda_codec *codec) { - kfree(codec->spec); + struct alc_spec *spec = codec->spec; + unsigned int i; + + if (! spec) + return; + + if (spec->kctl_alloc) { + for (i = 0; i < spec->num_kctl_used; i++) + kfree(spec->kctl_alloc[i].name); + kfree(spec->kctl_alloc); + } + kfree(spec); } /* @@ -921,153 +1215,915 @@ static struct hda_codec_ops alc_patch_ops = { #endif }; + +/* + * Test configuration for debugging + * + * Almost all inputs/outputs are enabled. I/O pins can be configured via + * enum controls. + */ +#ifdef CONFIG_SND_DEBUG +static hda_nid_t alc880_test_dac_nids[4] = { + 0x02, 0x03, 0x04, 0x05 +}; + +static struct hda_input_mux alc880_test_capture_source = { + .num_items = 5, + .items = { + { "In-1", 0x0 }, + { "In-2", 0x1 }, + { "In-3", 0x2 }, + { "In-4", 0x3 }, + { "CD", 0x4 }, + }, +}; + +static struct alc_channel_mode alc880_test_modes[4] = { + { 2, NULL }, + { 4, NULL }, + { 6, NULL }, + { 8, NULL }, +}; + +static int alc_test_pin_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[] = { + "N/A", "Line Out", "HP Out", + "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item >= 8) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int alc_test_pin_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int pin_ctl, item = 0; + + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pin_ctl & AC_PINCTL_OUT_EN) { + if (pin_ctl & AC_PINCTL_HP_EN) + item = 2; + else + item = 1; + } else if (pin_ctl & AC_PINCTL_IN_EN) { + switch (pin_ctl & AC_PINCTL_VREFEN) { + case AC_PINCTL_VREF_HIZ: item = 3; break; + case AC_PINCTL_VREF_50: item = 4; break; + case AC_PINCTL_VREF_GRD: item = 5; break; + case AC_PINCTL_VREF_80: item = 6; break; + case AC_PINCTL_VREF_100: item = 7; break; + } + } + ucontrol->value.enumerated.item[0] = item; + return 0; +} + +static int alc_test_pin_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + static unsigned int ctls[] = { + 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_50, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_100, + }; + unsigned int old_ctl, new_ctl; + + old_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + new_ctl = ctls[ucontrol->value.enumerated.item[0]]; + if (old_ctl != new_ctl) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000); + return 1; + } + return 0; +} + +static int alc_test_pin_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[] = { + "Front", "Surround", "CLFE", "Side" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int alc_test_pin_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int sel; + + sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); + ucontrol->value.enumerated.item[0] = sel & 3; + return 0; +} + +static int alc_test_pin_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int sel; + + sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; + if (ucontrol->value.enumerated.item[0] != sel) { + sel = ucontrol->value.enumerated.item[0] & 3; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel); + return 1; + } + return 0; +} + +#define PIN_CTL_TEST(xname,nid) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = alc_test_pin_ctl_info, \ + .get = alc_test_pin_ctl_get, \ + .put = alc_test_pin_ctl_put, \ + .private_value = nid \ + } + +#define PIN_SRC_TEST(xname,nid) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = alc_test_pin_src_info, \ + .get = alc_test_pin_src_get, \ + .put = alc_test_pin_src_put, \ + .private_value = nid \ + } + +static snd_kcontrol_new_t alc880_test_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + ALC_BIND_MUTE("CLFE Playback Volume", 0x0e, 2, HDA_INPUT), + ALC_BIND_MUTE("Side Playback Volume", 0x0f, 2, HDA_INPUT), + PIN_CTL_TEST("Front Pin Mode", 0x14), + PIN_CTL_TEST("Surround Pin Mode", 0x15), + PIN_CTL_TEST("CLFE Pin Mode", 0x16), + PIN_CTL_TEST("Side Pin Mode", 0x17), + PIN_CTL_TEST("In-1 Pin Mode", 0x18), + PIN_CTL_TEST("In-2 Pin Mode", 0x19), + PIN_CTL_TEST("In-3 Pin Mode", 0x1a), + PIN_CTL_TEST("In-4 Pin Mode", 0x1b), + PIN_SRC_TEST("In-1 Pin Source", 0x18), + PIN_SRC_TEST("In-2 Pin Source", 0x19), + PIN_SRC_TEST("In-3 Pin Source", 0x1a), + PIN_SRC_TEST("In-4 Pin Source", 0x1b), + HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 2, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc880_ch_mode_info, + .get = alc880_ch_mode_get, + .put = alc880_ch_mode_put, + }, + { } /* end */ +}; + +static struct hda_verb alc880_test_init_verbs[] = { + /* Unmute inputs of 0x0c - 0x0f */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Vol output for 0x0c-0x0f */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Set output pins 0x14-0x17 */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Unmute output pins 0x14-0x17 */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Set input pins 0x18-0x1c */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Mute input pins 0x18-0x1b */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* ADC set up */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Analog input/passthru */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + { } +}; +#endif + /* */ static struct hda_board_config alc880_cfg_tbl[] = { /* Back 3 jack, front 2 jack */ { .modelname = "3stack", .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST }, - { .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST }, /* Back 3 jack, front 2 jack (Internal add Aux-In) */ - { .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST }, + { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST }, + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST }, /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */ { .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG }, /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ - { .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG }, - { .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG }, + { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG }, /* Back 5 jack, front 2 jack */ { .modelname = "5stack", .config = ALC880_5ST }, - { .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST }, - { .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST }, - { .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST }, - { .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST }, + { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST }, /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */ { .modelname = "5stack-digout", .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG }, - { .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG }, + { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, { .modelname = "w810", .config = ALC880_W810 }, - { .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 }, + { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 }, + + { .modelname = "z71v", .config = ALC880_Z71V }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V }, + + { .modelname = "6statack-digout", .config = ALC880_6ST_DIG }, + { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG }, + { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG }, + { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG }, + + { .modelname = "asus", .config = ALC880_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V }, + + { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG }, + { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG }, + + { .modelname = "F1734", .config = ALC880_F1734 }, + { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 }, + +#ifdef CONFIG_SND_DEBUG + { .modelname = "test", .config = ALC880_TEST }, +#endif {} }; +/* + * configuration template - to be copied to the spec instance + */ +struct alc_config_preset { + snd_kcontrol_new_t *mixers[4]; + const struct hda_verb *init_verbs[4]; + unsigned int num_dacs; + hda_nid_t *dac_nids; + hda_nid_t dig_out_nid; /* optional */ + hda_nid_t hp_nid; /* optional */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + unsigned int num_channel_mode; + const struct alc_channel_mode *channel_mode; + const struct hda_input_mux *input_mux; +}; + +static struct alc_config_preset alc880_presets[] = { + [ALC880_3ST] = { + .mixers = { alc880_three_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_3ST_DIG] = { + .mixers = { alc880_three_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_5ST] = { + .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer}, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), + .channel_mode = alc880_fivestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_5ST_DIG] = { + .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), + .channel_mode = alc880_fivestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_6ST_DIG] = { + .mixers = { alc880_six_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), + .dac_nids = alc880_6st_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), + .channel_mode = alc880_sixstack_modes, + .input_mux = &alc880_6stack_capture_source, + }, + [ALC880_W810] = { + .mixers = { alc880_w810_base_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), + .dac_nids = alc880_w810_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), + .channel_mode = alc880_w810_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_Z71V] = { + .mixers = { alc880_z71v_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs, + alc880_gpio2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), + .dac_nids = alc880_z71v_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_F1734] = { + .mixers = { alc880_f1734_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_f1734_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids), + .dac_nids = alc880_f1734_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS_DIG] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS_W1V] = { + .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_UNIWILL_DIG] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .input_mux = &alc880_capture_source, + }, +#ifdef CONFIG_SND_DEBUG + [ALC880_TEST] = { + .mixers = { alc880_test_mixer }, + .init_verbs = { alc880_test_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_test_dac_nids), + .dac_nids = alc880_test_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_test_modes), + .channel_mode = alc880_test_modes, + .input_mux = &alc880_test_capture_source, + }, +#endif +}; + +/* + * Automatic parse of I/O pins from the BIOS configuration + */ + +#define NUM_CONTROL_ALLOC 32 +#define NUM_VERB_ALLOC 32 + +enum { + ALC_CTL_WIDGET_VOL, + ALC_CTL_WIDGET_MUTE, + ALC_CTL_BIND_MUTE, +}; +static snd_kcontrol_new_t alc880_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + ALC_BIND_MUTE(NULL, 0, 0, 0), +}; + +/* add dynamic controls */ +static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val) +{ + snd_kcontrol_new_t *knew; + + if (spec->num_kctl_used >= spec->num_kctl_alloc) { + int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; + + knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ + if (! knew) + return -ENOMEM; + if (spec->kctl_alloc) { + memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); + kfree(spec->kctl_alloc); + } + spec->kctl_alloc = knew; + spec->num_kctl_alloc = num; + } + + knew = &spec->kctl_alloc[spec->num_kctl_used]; + *knew = alc880_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (! knew->name) + return -ENOMEM; + knew->private_value = val; + spec->num_kctl_used++; + return 0; +} + +#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) +#define alc880_fixed_pin_idx(nid) ((nid) - 0x14) +#define alc880_is_multi_pin(nid) ((nid) >= 0x18) +#define alc880_multi_pin_idx(nid) ((nid) - 0x18) +#define alc880_is_input_pin(nid) ((nid) >= 0x18) +#define alc880_input_pin_idx(nid) ((nid) - 0x18) +#define alc880_idx_to_dac(nid) ((nid) + 0x02) +#define alc880_dac_to_idx(nid) ((nid) - 0x02) +#define alc880_idx_to_mixer(nid) ((nid) + 0x0c) +#define alc880_idx_to_selector(nid) ((nid) + 0x10) +#define ALC880_PIN_CD_NID 0x1c + +/* fill in the dac_nids table from the parsed pin configuration */ +static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int assigned[4]; + int i, j; + + memset(assigned, 0, sizeof(assigned)); + + /* check the pins hardwired to audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (alc880_is_fixed_pin(nid)) { + int idx = alc880_fixed_pin_idx(nid); + spec->multiout.dac_nids[i] = alc880_dac_to_idx(idx); + assigned[idx] = 1; + } + } + /* left pins can be connect to any audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (alc880_is_fixed_pin(nid)) + continue; + /* search for an empty channel */ + for (j = 0; j < cfg->line_outs; j++) { + if (! assigned[j]) { + spec->multiout.dac_nids[i] = alc880_idx_to_dac(j); + assigned[j] = 1; + break; + } + } + } + spec->multiout.num_dacs = cfg->line_outs; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; + hda_nid_t nid; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + if (! spec->multiout.dac_nids[i]) + continue; + nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); + if (i == 2) { + /* Center/LFE */ + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT))) < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) + return err; + } + } + + return 0; +} + +/* add playback controls for HP output */ +static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin) +{ + hda_nid_t nid; + int err; + + if (! pin) + return 0; + + if (alc880_is_fixed_pin(pin)) { + nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); + if (! spec->multiout.dac_nids[0]) { + /* use this as the primary output */ + spec->multiout.dac_nids[0] = nid; + if (! spec->multiout.num_dacs) + spec->multiout.num_dacs = 1; + } else + /* specify the DAC as the extra HP output */ + spec->multiout.hp_nid = nid; + /* control HP volume/switch on the output mixer amp */ + nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) + return err; + } else if (alc880_is_multi_pin(pin)) { + /* set manual connection */ + if (! spec->multiout.dac_nids[0]) { + /* use this as the primary output */ + spec->multiout.dac_nids[0] = alc880_idx_to_dac(alc880_multi_pin_idx(pin)); + if (! spec->multiout.num_dacs) + spec->multiout.num_dacs = 1; + } + /* we have only a switch on HP-out PIN */ + if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0) + return err; + } + return 0; +} + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname) +{ + char name[32]; + int err, idx; + + sprintf(name, "%s Playback Volume", ctlname); + idx = alc880_input_pin_idx(pin); + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0) + return err; + return 0; +} + +/* create playback/capture controls for input pins */ +static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) +{ + static char *labels[AUTO_PIN_LAST] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" + }; + struct hda_input_mux *imux = &spec->private_imux; + int i, err; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (alc880_is_input_pin(cfg->input_pins[i])) { + err = new_analog_input(spec, cfg->input_pins[i], labels[i]); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]); + imux->num_items++; + } + } + return 0; +} + +static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + /* need the manual connection? */ + if (alc880_is_multi_pin(nid)) { + struct alc_spec *spec = codec->spec; + int idx = alc880_multi_pin_idx(nid); + snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, + AC_VERB_SET_CONNECT_SEL, + alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); + } +} + +static void alc880_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} + +static void alc880_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pin; + if (pin) /* connect to front */ + alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); +} + +static void alc880_auto_init_analog_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc880_is_input_pin(nid)) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN); + if (nid != ALC880_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } + } +} + +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, or a negative error code */ +static int alc880_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0) + return err; + if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0) + return err; + if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin) + return 0; /* can't find valid BIOS pin config */ + if ((err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || + (err = alc880_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 || + (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = ALC880_DIGIN_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +/* init callback for auto-configuration model -- overriding the default init */ +static int alc880_auto_init(struct hda_codec *codec) +{ + alc_init(codec); + alc880_auto_init_multi_out(codec); + alc880_auto_init_hp_out(codec); + alc880_auto_init_analog_input(codec); + return 0; +} + +/* + * OK, here we have finally the patch for ALC880 + */ + static int patch_alc880(struct hda_codec *codec) { struct alc_spec *spec; int board_config; + int i, err; spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; + init_MUTEX(&spec->bind_mutex); codec->spec = spec; board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl); - if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: Unknown model for ALC880\n"); - board_config = ALC880_MINIMAL; + if (board_config < 0 || board_config >= ALC880_MODEL_LAST) { + printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n"); + board_config = ALC880_AUTO; } - switch (board_config) { - case ALC880_W810: - spec->mixers[spec->num_mixers] = alc880_w810_base_mixer; - spec->num_mixers++; - break; - case ALC880_5ST: - case ALC880_5ST_DIG: - spec->mixers[spec->num_mixers] = alc880_five_stack_mixer; - spec->num_mixers++; - break; - default: - spec->mixers[spec->num_mixers] = alc880_base_mixer; - spec->num_mixers++; - break; + if (board_config == ALC880_AUTO) { + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (! err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n"); + board_config = ALC880_3ST; + } } - switch (board_config) { - case ALC880_3ST_DIG: - case ALC880_5ST_DIG: - case ALC880_W810: - spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; - break; - default: - break; - } + if (board_config != ALC880_AUTO) { + /* set up from the preset table */ + const struct alc_config_preset *preset; - switch (board_config) { - case ALC880_3ST: - case ALC880_3ST_DIG: - case ALC880_5ST: - case ALC880_5ST_DIG: - case ALC880_W810: - spec->front_panel = 1; - break; - default: - break; - } + preset = &alc880_presets[board_config]; - switch (board_config) { - case ALC880_5ST: - case ALC880_5ST_DIG: - spec->init_verbs = alc880_init_verbs_five_stack; - spec->channel_mode = alc880_fivestack_modes; - spec->num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes); - break; - case ALC880_W810: - spec->init_verbs = alc880_w810_init_verbs; - spec->channel_mode = alc880_w810_modes; - spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes); - break; - default: - spec->init_verbs = alc880_init_verbs_three_stack; - spec->channel_mode = alc880_threestack_modes; - spec->num_channel_mode = ARRAY_SIZE(alc880_threestack_modes); - break; + for (i = 0; preset->mixers[i]; i++) { + snd_assert(spec->num_mixers < ARRAY_SIZE(spec->mixers), break); + spec->mixers[spec->num_mixers++] = preset->mixers[i]; + } + for (i = 0; preset->init_verbs[i]; i++) { + snd_assert(spec->num_init_verbs < ARRAY_SIZE(spec->init_verbs), break); + spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i]; + } + + spec->channel_mode = preset->channel_mode; + spec->num_channel_mode = preset->num_channel_mode; + + spec->multiout.max_channels = spec->channel_mode[0].channels; + + spec->multiout.num_dacs = preset->num_dacs; + spec->multiout.dac_nids = preset->dac_nids; + spec->multiout.dig_out_nid = preset->dig_out_nid; + spec->multiout.hp_nid = preset->hp_nid; + + spec->input_mux = preset->input_mux; + + spec->num_adc_nids = preset->num_adc_nids; + spec->adc_nids = preset->adc_nids; } spec->stream_name_analog = "ALC880 Analog"; @@ -1078,34 +2134,64 @@ static int patch_alc880(struct hda_codec *codec) spec->stream_digital_playback = &alc880_pcm_digital_playback; spec->stream_digital_capture = &alc880_pcm_digital_capture; - spec->multiout.max_channels = spec->channel_mode[0].channels; - - switch (board_config) { - case ALC880_W810: - spec->multiout.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids); - spec->multiout.dac_nids = alc880_w810_dac_nids; - // No dedicated headphone socket - it's shared with built-in speakers. - break; - default: - spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids); - spec->multiout.dac_nids = alc880_dac_nids; - spec->multiout.hp_nid = 0x03; /* rear-surround NID */ - break; + if (! spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = snd_hda_param_read(codec, alc880_adc_nids[0], + AC_PAR_AUDIO_WIDGET_CAP); + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc880_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); + spec->mixers[spec->num_mixers] = alc880_capture_alt_mixer; + spec->num_mixers++; + } else { + spec->adc_nids = alc880_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); + spec->mixers[spec->num_mixers] = alc880_capture_mixer; + spec->num_mixers++; + } } - spec->input_mux = &alc880_capture_source; - spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); - spec->adc_nids = alc880_adc_nids; - codec->patch_ops = alc_patch_ops; + if (board_config == ALC880_AUTO) + codec->patch_ops.init = alc880_auto_init; return 0; } + /* * ALC260 support */ +static hda_nid_t alc260_dac_nids[1] = { + /* front */ + 0x02, +}; + +static hda_nid_t alc260_adc_nids[1] = { + /* ADC0 */ + 0x04, +}; + +static hda_nid_t alc260_hp_adc_nids[1] = { + /* ADC1 */ + 0x05, +}; + +#define ALC260_DIGOUT_NID 0x03 +#define ALC260_DIGIN_NID 0x06 + +static struct hda_input_mux alc260_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + /* * This is just place-holder, so there's something for alc_build_pcms to look * at when it calculates the maximum number of channels. ALC260 has no mixer @@ -1116,11 +2202,9 @@ static struct alc_channel_mode alc260_modes[1] = { { 2, NULL }, }; -snd_kcontrol_new_t alc260_base_mixer[] = { +static snd_kcontrol_new_t alc260_base_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), - /* use LINE2 for the output */ - /* HDA_CODEC_MUTE("Front Playback Switch", 0x0f, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), @@ -1132,9 +2216,9 @@ snd_kcontrol_new_t alc260_base_mixer[] = { HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), { @@ -1147,60 +2231,91 @@ snd_kcontrol_new_t alc260_base_mixer[] = { { } /* end */ }; +static snd_kcontrol_new_t alc260_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + static struct hda_verb alc260_init_verbs[] = { /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* LINE-2 is used for line-out in rear */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* select line-out */ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* LINE-OUT pin */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* enable HP */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, /* enable Mono */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* unmute amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* set connection select to line in (default select for this ADC) */ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute Line-Out mixer amp left and right (volume = 0) */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* unmute HP mixer amp left and right (volume = 0) */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* unmute Mono mixer amp left and right (volume = 0) */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* mute LINE-2 out */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* mute capture amp left and right */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* set connection select to line in (default select for this ADC) */ + {0x05, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* set vol=0 Line-Out mixer amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* set vol=0 HP mixer amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* set vol=0 Mono mixer amp left and right */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* unmute LINE-2 out pin */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */ - /* unmute CD */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* unmute Line In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - /* unmute Mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* mute CD */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* mute Line In */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + /* mute Mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* Unmute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* mute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* mute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* mute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, { } }; @@ -1208,30 +2323,52 @@ static struct hda_pcm_stream alc260_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .nid = 0x2, }; static struct hda_pcm_stream alc260_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .nid = 0x4, +}; + +static struct hda_board_config alc260_cfg_tbl[] = { + { .modelname = "hp", .config = ALC260_HP }, + { .pci_subvendor = 0x103c, .config = ALC260_HP }, + {} }; static int patch_alc260(struct hda_codec *codec) { struct alc_spec *spec; + int board_config; spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; + init_MUTEX(&spec->bind_mutex); codec->spec = spec; - spec->mixers[spec->num_mixers] = alc260_base_mixer; - spec->num_mixers++; + board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl); + if (board_config < 0 || board_config >= ALC260_MODEL_LAST) { + snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n"); + board_config = ALC260_BASIC; + } + + switch (board_config) { + case ALC260_HP: + spec->mixers[spec->num_mixers] = alc260_hp_mixer; + spec->num_mixers++; + break; + default: + spec->mixers[spec->num_mixers] = alc260_base_mixer; + spec->num_mixers++; + break; + } + + spec->init_verbs[0] = alc260_init_verbs; + spec->num_init_verbs = 1; - spec->init_verbs = alc260_init_verbs; spec->channel_mode = alc260_modes; spec->num_channel_mode = ARRAY_SIZE(alc260_modes); @@ -1244,14 +2381,23 @@ static int patch_alc260(struct hda_codec *codec) spec->multiout.dac_nids = alc260_dac_nids; spec->input_mux = &alc260_capture_source; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); - spec->adc_nids = alc260_adc_nids; + switch (board_config) { + case ALC260_HP: + spec->num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids); + spec->adc_nids = alc260_hp_adc_nids; + break; + default: + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); + spec->adc_nids = alc260_adc_nids; + break; + } codec->patch_ops = alc_patch_ops; return 0; } + /* * ALC882 support * @@ -1324,15 +2470,15 @@ static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u */ static snd_kcontrol_new_t alc882_base_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_OUTPUT), HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x17, 0x0, HDA_OUTPUT), + ALC_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), @@ -1364,89 +2510,80 @@ static snd_kcontrol_new_t alc882_base_mixer[] = { static struct hda_verb alc882_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - - /* Front Pin: to output mode */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Front Pin: mute amp left and right (no volume) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* select Front mixer (0x0c, index 0) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Rear Pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Rear Pin: mute amp left and right (no volume) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* select Rear mixer (0x0d, index 1) */ + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* CLFE Pin: mute amp left and right (no volume) */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* select CLFE mixer (0x0e, index 2) */ + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Side Pin: mute amp left and right (no volume) */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* select Side mixer (0x0f, index 3) */ + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Headphone Pin */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Headphone Pin: mute amp left and right (no volume) */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* select Front mixer (0x0c, index 0) */ + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Mic (rear) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Front Mic pin widget for input and vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* FIXME: use matrix-type input source selection */ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* ADC1: unmute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* ADC2: unmute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* ADC3: unmute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - - /* Unmute front loopback */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute rear loopback */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Mute CLFE loopback */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - /* Unmute side loopback */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, { } }; @@ -1459,6 +2596,7 @@ static int patch_alc882(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + init_MUTEX(&spec->bind_mutex); codec->spec = spec; spec->mixers[spec->num_mixers] = alc882_base_mixer; @@ -1466,8 +2604,9 @@ static int patch_alc882(struct hda_codec *codec) spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; spec->dig_in_nid = ALC880_DIGIN_NID; - spec->front_panel = 1; - spec->init_verbs = alc882_init_verbs; + spec->init_verbs[0] = alc882_init_verbs; + spec->num_init_verbs = 1; + spec->channel_mode = alc882_ch_modes; spec->num_channel_mode = ARRAY_SIZE(alc882_ch_modes); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c new file mode 100644 index 0000000..013be2e --- /dev/null +++ b/sound/pci/hda/patch_sigmatel.c @@ -0,0 +1,666 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for SigmaTel STAC92xx + * + * Copyright (c) 2005 Embedded Alley Solutions, Inc. + * <matt@embeddedalley.com> + * + * Based on patch_cmedia.c and patch_realtek.c + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +#undef STAC_TEST + +struct sigmatel_spec { + /* playback */ + struct hda_multi_out multiout; + hda_nid_t playback_nid; + + /* capture */ + hda_nid_t *adc_nids; + unsigned int num_adcs; + hda_nid_t *mux_nids; + unsigned int num_muxes; + hda_nid_t capture_nid; + hda_nid_t dig_in_nid; + + /* power management*/ + hda_nid_t *pstate_nids; + unsigned int num_pstates; + + /* pin widgets */ + hda_nid_t *pin_nids; + unsigned int num_pins; +#ifdef STAC_TEST + unsigned int *pin_configs; +#endif + + /* codec specific stuff */ + struct hda_verb *init; + snd_kcontrol_new_t *mixer; + + /* capture source */ + struct hda_input_mux input_mux; + char input_labels[HDA_MAX_NUM_INPUTS][16]; + unsigned int cur_mux[2]; + + /* channel mode */ + unsigned int num_ch_modes; + unsigned int cur_ch_mode; + const struct sigmatel_channel_mode *channel_modes; + + struct hda_pcm pcm_rec[1]; /* PCM information */ +}; + +static hda_nid_t stac9200_adc_nids[1] = { + 0x03, +}; + +static hda_nid_t stac9200_mux_nids[1] = { + 0x0c, +}; + +static hda_nid_t stac9200_dac_nids[1] = { + 0x02, +}; + +static hda_nid_t stac9200_pstate_nids[3] = { + 0x01, 0x02, 0x03, +}; + +static hda_nid_t stac9200_pin_nids[8] = { + 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, +}; + +static hda_nid_t stac922x_adc_nids[2] = { + 0x06, 0x07, +}; + +static hda_nid_t stac922x_mux_nids[2] = { + 0x12, 0x13, +}; + +static hda_nid_t stac922x_dac_nids[4] = { + 0x02, 0x03, 0x04, 0x05, +}; + +static hda_nid_t stac922x_pstate_nids[8] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11, +}; + +static hda_nid_t stac922x_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x15, 0x1b, +}; + +static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); +} + +static struct hda_verb stac9200_ch2_init[] = { + /* set dac0mux for dac converter */ + { 0x07, 0x701, 0x00}, + {} +}; + +static struct hda_verb stac922x_ch2_init[] = { + /* set master volume and direct control */ + { 0x16, 0x70f, 0xff}, + {} +}; + +struct sigmatel_channel_mode { + unsigned int channels; + const struct hda_verb *sequence; +}; + +static snd_kcontrol_new_t stac9200_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT), + { } /* end */ +}; + +static snd_kcontrol_new_t stac922x_mixer[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static int stac92xx_build_controls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + err = snd_hda_add_new_ctls(codec, spec->mixer); + if (err < 0) + return err; + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); + if (err < 0) + return err; + } + return 0; +} + +#ifdef STAC_TEST +static unsigned int stac9200_pin_configs[8] = { + 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, + 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, +}; + +static unsigned int stac922x_pin_configs[14] = { + 0x40000100, 0x40000100, 0x40000100, 0x01114010, + 0x01813122, 0x40000100, 0x01447010, 0x01c47010, + 0x40000100, 0x40000100, +}; + +static void stac92xx_set_config_regs(struct hda_codec *codec) +{ + int i; + struct sigmatel_spec *spec = codec->spec; + unsigned int pin_cfg; + + for (i=0; i < spec->num_pins; i++) { + snd_hda_codec_write(codec, spec->pin_nids[i], 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, + spec->pin_configs[i] & 0x000000ff); + snd_hda_codec_write(codec, spec->pin_nids[i], 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, + (spec->pin_configs[i] & 0x0000ff00) >> 8); + snd_hda_codec_write(codec, spec->pin_nids[i], 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, + (spec->pin_configs[i] & 0x00ff0000) >> 16); + snd_hda_codec_write(codec, spec->pin_nids[i], 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, + spec->pin_configs[i] >> 24); + pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0, + AC_VERB_GET_CONFIG_DEFAULT, + 0x00); + printk("pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg); + } +} +#endif + +static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value) +{ + unsigned int pin_ctl; + + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl | value); + + return 0; +} + +static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT; + unsigned int vref_ctl = AC_PINCTL_VREF_HIZ; + + if (vref_caps & AC_PINCAP_VREF_100) + vref_ctl = AC_PINCTL_VREF_100; + else if (vref_caps & AC_PINCAP_VREF_80) + vref_ctl = AC_PINCTL_VREF_80; + else if (vref_caps & AC_PINCAP_VREF_50) + vref_ctl = AC_PINCTL_VREF_50; + else if (vref_caps & AC_PINCAP_VREF_GRD) + vref_ctl = AC_PINCTL_VREF_GRD; + + stac92xx_set_pinctl(codec, nid, vref_ctl); + + return 0; +} + +/* + * retrieve the default device type from the default config value + */ +#define get_defcfg_type(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) + +static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg) +{ + struct sigmatel_spec *spec = codec->spec; + u32 location = get_defcfg_location(pin_cfg); + char *label; + const char *type = NULL; + int ainput = 0; + + switch(get_defcfg_type(pin_cfg)) { + case AC_JACK_HP_OUT: + /* Enable HP amp */ + stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN); + /* Fall through */ + case AC_JACK_SPDIF_OUT: + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + /* Enable output */ + stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + break; + case AC_JACK_SPDIF_IN: + stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); + break; + case AC_JACK_MIC_IN: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + type = "Front Mic"; + else + type = "Mic"; + ainput = 1; + /* Set vref */ + stac92xx_set_vref(codec, nid); + stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); + break; + case AC_JACK_CD: + type = "CD"; + ainput = 1; + stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); + break; + case AC_JACK_LINE_IN: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + type = "Front Line"; + else + type = "Line"; + ainput = 1; + stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); + break; + case AC_JACK_AUX: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + type = "Front Aux"; + else + type = "Aux"; + ainput = 1; + stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); + break; + } + + if (ainput) { + hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; + int i, j, num_cons, index = -1; + if (!type) + type = "Input"; + label = spec->input_labels[spec->input_mux.num_items]; + strcpy(label, type); + spec->input_mux.items[spec->input_mux.num_items].label = label; + for (i=0; i<spec->num_muxes; i++) { + num_cons = snd_hda_get_connections(codec, spec->mux_nids[i], con_lst, HDA_MAX_NUM_INPUTS); + for (j=0; j<num_cons; j++) + if (con_lst[j] == nid) { + index = j; + break; + } + if (index >= 0) + break; + } + spec->input_mux.items[spec->input_mux.num_items].index = index; + spec->input_mux.num_items++; + } + + return 0; +} + +static int stac92xx_config_pins(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + unsigned int pin_cfg; + + for (i=0; i < spec->num_pins; i++) { + /* Default to disabled */ + snd_hda_codec_write(codec, spec->pin_nids[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + 0x00); + + pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0, + AC_VERB_GET_CONFIG_DEFAULT, + 0x00); + if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE) + continue; /* Move on */ + + stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg); + } + + return 0; +} + +static int stac92xx_init(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i=0; i < spec->num_pstates; i++) + snd_hda_codec_write(codec, spec->pstate_nids[i], 0, + AC_VERB_SET_POWER_STATE, 0x00); + + mdelay(100); + + snd_hda_sequence_write(codec, spec->init); + +#ifdef STAC_TEST + stac92xx_set_config_regs(codec); +#endif + + stac92xx_config_pins(codec); + + return 0; +} + +/* + * Analog playback callbacks + */ +static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital playback callbacks + */ +static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + + +/* + * Analog capture callbacks + */ +static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + struct sigmatel_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + return 0; +} + +static struct hda_pcm_stream stac92xx_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in stac92xx_build_pcms */ + .ops = { + .open = stac92xx_dig_playback_pcm_open, + .close = stac92xx_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream stac92xx_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in stac92xx_build_pcms */ +}; + +static struct hda_pcm_stream stac92xx_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x02, /* NID to query formats and rates */ + .ops = { + .open = stac92xx_playback_pcm_open, + .prepare = stac92xx_playback_pcm_prepare, + .cleanup = stac92xx_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream stac92xx_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x06, /* NID to query formats and rates */ + .ops = { + .prepare = stac92xx_capture_pcm_prepare, + .cleanup = stac92xx_capture_pcm_cleanup + }, +}; + +static int stac92xx_build_pcms(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "STAC92xx"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid; + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms++; + info++; + info->name = "STAC92xx Digital"; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + } + + return 0; +} + +static void stac92xx_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +static struct hda_codec_ops stac92xx_patch_ops = { + .build_controls = stac92xx_build_controls, + .build_pcms = stac92xx_build_pcms, + .init = stac92xx_init, + .free = stac92xx_free, +}; + +static int patch_stac9200(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = stac9200_dac_nids; + spec->multiout.dig_out_nid = 0x05; + spec->dig_in_nid = 0x04; + spec->adc_nids = stac9200_adc_nids; + spec->mux_nids = stac9200_mux_nids; + spec->num_muxes = 1; + spec->input_mux.num_items = 0; + spec->pstate_nids = stac9200_pstate_nids; + spec->num_pstates = 3; + spec->pin_nids = stac9200_pin_nids; +#ifdef STAC_TEST + spec->pin_configs = stac9200_pin_configs; +#endif + spec->num_pins = 8; + spec->init = stac9200_ch2_init; + spec->mixer = stac9200_mixer; + spec->playback_nid = 0x02; + spec->capture_nid = 0x03; + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + +static int patch_stac922x(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 4; + spec->multiout.dac_nids = stac922x_dac_nids; + spec->multiout.dig_out_nid = 0x08; + spec->dig_in_nid = 0x09; + spec->adc_nids = stac922x_adc_nids; + spec->mux_nids = stac922x_mux_nids; + spec->num_muxes = 2; + spec->input_mux.num_items = 0; + spec->pstate_nids = stac922x_pstate_nids; + spec->num_pstates = 8; + spec->pin_nids = stac922x_pin_nids; +#ifdef STAC_TEST + spec->pin_configs = stac922x_pin_configs; +#endif + spec->num_pins = 10; + spec->init = stac922x_ch2_init; + spec->mixer = stac922x_mixer; + spec->playback_nid = 0x02; + spec->capture_nid = 0x06; + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_sigmatel[] = { + { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 }, + { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x }, + { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x }, + { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x }, + { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, + { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, + { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, + {} /* terminator */ +}; diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 7799517..289b0b5 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -30,16 +30,39 @@ #include <sound/core.h> #include "ice1712.h" +#include "envy24ht.h" #include "amp.h" +static void wm_put(ice1712_t *ice, int reg, unsigned short val) +{ + unsigned short cval; + cval = (reg << 9) | val; + snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff); +} static int __devinit snd_vt1724_amp_init(ice1712_t *ice) { + static unsigned short wm_inits[] = { + WM_ATTEN_L, 0x0000, /* 0 db */ + WM_ATTEN_R, 0x0000, /* 0 db */ + WM_DAC_CTRL, 0x0008, /* 24bit I2S */ + WM_INT_CTRL, 0x0001, /* 24bit I2S */ + }; + + unsigned int i; + /* only use basic functionality for now */ ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ ice->num_total_adcs = 2; + /* Chaintech AV-710 has another codecs, which need initialization */ + /* initialize WM8728 codec */ + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) { + for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) + wm_put(ice, wm_inits[i], wm_inits[i+1]); + } + return 0; } @@ -54,6 +77,13 @@ static int __devinit snd_vt1724_amp_add_controls(ice1712_t *ice) /* entry point */ struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { { + .subvendor = VT1724_SUBDEVICE_AV710, + .name = "Chaintech AV-710", + .model = "av710", + .chip_init = snd_vt1724_amp_init, + .build_controls = snd_vt1724_amp_add_controls, + }, + { .subvendor = VT1724_SUBDEVICE_AUDIO2000, .name = "AMP Ltd AUDIO2000", .model = "amp2000", diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h index d58d433..a0fc89b 100644 --- a/sound/pci/ice1712/amp.h +++ b/sound/pci/ice1712/amp.h @@ -24,9 +24,23 @@ * */ -#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000}," +#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000},"\ + "{Chaintech,AV-710}," +#if 0 #define VT1724_SUBDEVICE_AUDIO2000 0x12142417 /* Advanced Micro Peripherals Ltd AUDIO2000 */ +#else +#define VT1724_SUBDEVICE_AUDIO2000 0x00030003 /* a dummy ID for AMP Audio2000 */ +#endif +#define VT1724_SUBDEVICE_AV710 0x12142417 /* AV710 - the same ID with Audio2000! */ + +/* WM8728 on I2C for AV710 */ +#define WM_DEV 0x36 + +#define WM_ATTEN_L 0x00 +#define WM_ATTEN_R 0x01 +#define WM_DAC_CTRL 0x02 +#define WM_INT_CTRL 0x03 extern struct snd_ice1712_card_info snd_vt1724_amp_cards[]; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 79fba6b..a2545a5 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2748,7 +2748,7 @@ static struct pci_driver driver = { static int __init alsa_card_ice1712_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_ice1712_exit(void) diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 8bb1c58..5ad4728 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -373,6 +373,11 @@ struct _snd_ice1712 { unsigned short master[2]; unsigned short vol[8]; } aureon; + /* AC97 register cache for Phase28 */ + struct phase28_spec { + unsigned short master[2]; + unsigned short vol[8]; + } phase28; /* Hoontech-specific setting */ struct hoontech_spec { unsigned char boxbits[4]; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 95500f0..79b5f12 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2328,7 +2328,7 @@ static struct pci_driver driver = { static int __init alsa_card_ice1724_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_ice1724_exit(void) diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index d1f9083..5bf734b 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -45,6 +45,47 @@ #include "envy24ht.h" #include "phase.h" +/* WM8770 registers */ +#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ +#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ +#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */ +#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */ +#define WM_PHASE_SWAP 0x12 /* DAC phase */ +#define WM_DAC_CTRL1 0x13 /* DAC control bits */ +#define WM_MUTE 0x14 /* mute controls */ +#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */ +#define WM_INT_CTRL 0x16 /* interface control */ +#define WM_MASTER 0x17 /* master clock and mode */ +#define WM_POWERDOWN 0x18 /* power-down controls */ +#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */ +#define WM_ADC_MUX 0x1b /* input MUX */ +#define WM_OUT_MUX1 0x1c /* output MUX */ +#define WM_OUT_MUX2 0x1e /* output MUX */ +#define WM_RESET 0x1f /* software reset */ + + +/* + * Logarithmic volume values for WM8770 + * Computed as 20 * Log10(255 / x) + */ +static unsigned char wm_vol[256] = { + 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, + 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, + 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, + 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +#define WM_VOL_MAX (sizeof(wm_vol) - 1) +#define WM_VOL_MUTE 0x8000 + static akm4xxx_t akm_phase22 __devinitdata = { .type = SND_AK4524, .num_dacs = 2, @@ -124,6 +165,684 @@ static unsigned char phase22_eeprom[] __devinitdata = { 0x00, /* GPIO_STATE2 */ }; +static unsigned char phase28_eeprom[] __devinitdata = { + 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +/* + * write data in the SPI mode + */ +static void phase28_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits) +{ + unsigned int tmp; + int i; + + tmp = snd_ice1712_gpio_read(ice); + + snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK| + PHASE28_WM_CS)); + tmp |= PHASE28_WM_RW; + tmp &= ~cs; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + + for (i = bits - 1; i >= 0; i--) { + tmp &= ~PHASE28_SPI_CLK; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + if (data & (1 << i)) + tmp |= PHASE28_SPI_MOSI; + else + tmp &= ~PHASE28_SPI_MOSI; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + tmp |= PHASE28_SPI_CLK; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + } + + tmp &= ~PHASE28_SPI_CLK; + tmp |= cs; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + tmp |= PHASE28_SPI_CLK; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); +} + +/* + * get the current register value of WM codec + */ +static unsigned short wm_get(ice1712_t *ice, int reg) +{ + reg <<= 1; + return ((unsigned short)ice->akm[0].images[reg] << 8) | + ice->akm[0].images[reg + 1]; +} + +/* + * set the register value of WM codec + */ +static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val) +{ + phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16); +} + +/* + * set the register value of WM codec and remember it + */ +static void wm_put(ice1712_t *ice, int reg, unsigned short val) +{ + wm_put_nocache(ice, reg, val); + reg <<= 1; + ice->akm[0].images[reg] = val >> 8; + ice->akm[0].images[reg + 1] = val; +} + +static void wm_set_vol(ice1712_t *ice, unsigned int index, unsigned short vol, unsigned short master) +{ + unsigned char nvol; + + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + nvol = 0; + else + nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX]; + + wm_put(ice, index, nvol); + wm_put_nocache(ice, index, 0x180 | nvol); +} + +/* + * DAC mute control + */ +#define wm_pcm_mute_info phase28_mono_bool_info + +static int wm_pcm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + down(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_pcm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short nval, oval; + int change; + + snd_ice1712_save_gpio_status(ice); + oval = wm_get(ice, WM_MUTE); + nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10); + if ((change = (nval != oval))) + wm_put(ice, WM_MUTE, nval); + snd_ice1712_restore_gpio_status(ice); + + return change; +} + +/* + * Master volume attenuation mixer control + */ +static int wm_master_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WM_VOL_MAX; + return 0; +} + +static int wm_master_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int i; + for (i=0; i<2; i++) + ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE; + return 0; +} + +static int wm_master_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int ch, change = 0; + + snd_ice1712_save_gpio_status(ice); + for (ch = 0; ch < 2; ch++) { + if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) { + int dac; + ice->spec.phase28.master[ch] &= WM_VOL_MUTE; + ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch]; + for (dac = 0; dac < ice->num_total_dacs; dac += 2) + wm_set_vol(ice, WM_DAC_ATTEN + dac + ch, + ice->spec.phase28.vol[dac + ch], + ice->spec.phase28.master[ch]); + change = 1; + } + } + snd_ice1712_restore_gpio_status(ice); + return change; +} + +static int __devinit phase28_init(ice1712_t *ice) +{ + static unsigned short wm_inits_phase28[] = { + /* These come first to reduce init pop noise */ + 0x1b, 0x044, /* ADC Mux (AC'97 source) */ + 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ + 0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */ + + 0x18, 0x000, /* All power-up */ + + 0x16, 0x122, /* I2S, normal polarity, 24bit */ + 0x17, 0x022, /* 256fs, slave mode */ + 0x00, 0, /* DAC1 analog mute */ + 0x01, 0, /* DAC2 analog mute */ + 0x02, 0, /* DAC3 analog mute */ + 0x03, 0, /* DAC4 analog mute */ + 0x04, 0, /* DAC5 analog mute */ + 0x05, 0, /* DAC6 analog mute */ + 0x06, 0, /* DAC7 analog mute */ + 0x07, 0, /* DAC8 analog mute */ + 0x08, 0x100, /* master analog mute */ + 0x09, 0xff, /* DAC1 digital full */ + 0x0a, 0xff, /* DAC2 digital full */ + 0x0b, 0xff, /* DAC3 digital full */ + 0x0c, 0xff, /* DAC4 digital full */ + 0x0d, 0xff, /* DAC5 digital full */ + 0x0e, 0xff, /* DAC6 digital full */ + 0x0f, 0xff, /* DAC7 digital full */ + 0x10, 0xff, /* DAC8 digital full */ + 0x11, 0x1ff, /* master digital full */ + 0x12, 0x000, /* phase normal */ + 0x13, 0x090, /* unmute DAC L/R */ + 0x14, 0x000, /* all unmute */ + 0x15, 0x000, /* no deemphasis, no ZFLG */ + 0x19, 0x000, /* -12dB ADC/L */ + 0x1a, 0x000, /* -12dB ADC/R */ + (unsigned short)-1 + }; + + unsigned int tmp; + akm4xxx_t *ak; + unsigned short *p; + int i; + + ice->num_total_dacs = 8; + ice->num_total_adcs = 2; + + // Initialize analog chips + ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL); + if (!ak) + return -ENOMEM; + ice->akm_codecs = 1; + + snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */ + + /* reset the wm codec as the SPI mode */ + snd_ice1712_save_gpio_status(ice); + snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL)); + + tmp = snd_ice1712_gpio_read(ice); + tmp &= ~PHASE28_WM_RESET; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + tmp |= PHASE28_WM_CS; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + tmp |= PHASE28_WM_RESET; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + + p = wm_inits_phase28; + for (; *p != (unsigned short)-1; p += 2) + wm_put(ice, p[0], p[1]); + + snd_ice1712_restore_gpio_status(ice); + + ice->spec.phase28.master[0] = WM_VOL_MUTE; + ice->spec.phase28.master[1] = WM_VOL_MUTE; + for (i = 0; i < ice->num_total_dacs; i++) { + ice->spec.phase28.vol[i] = WM_VOL_MUTE; + wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]); + } + + return 0; +} + +/* + * DAC volume attenuation mixer control + */ +static int wm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int voices = kcontrol->private_value >> 8; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = voices; + uinfo->value.integer.min = 0; /* mute (-101dB) */ + uinfo->value.integer.max = 0x7F; /* 0dB */ + return 0; +} + +static int wm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int i, ofs, voices; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + for (i = 0; i < voices; i++) + ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE; + return 0; +} + +static int wm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int i, idx, ofs, voices; + int change = 0; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + snd_ice1712_save_gpio_status(ice); + for (i = 0; i < voices; i++) { + idx = WM_DAC_ATTEN + ofs + i; + if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) { + ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE; + ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i]; + wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i], + ice->spec.phase28.master[i]); + change = 1; + } + } + snd_ice1712_restore_gpio_status(ice); + return change; +} + +/* + * WM8770 mute control + */ +static int wm_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = kcontrol->private_value >> 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int voices, ofs, i; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xFF; + + for (i = 0; i < voices; i++) + ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; + return 0; +} + +static int wm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, voices, ofs, i; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xFF; + + snd_ice1712_save_gpio_status(ice); + for (i = 0; i < voices; i++) { + int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; + if (ucontrol->value.integer.value[i] != val) { + ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE; + ice->spec.phase28.vol[ofs + i] |= + ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; + wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i], + ice->spec.phase28.master[i]); + change = 1; + } + } + snd_ice1712_restore_gpio_status(ice); + + return change; +} + +/* + * WM8770 master mute control + */ +static int wm_master_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm_master_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1; + return 0; +} + +static int wm_master_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change = 0, i; + + snd_ice1712_save_gpio_status(ice); + for (i = 0; i < 2; i++) { + int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1; + if (ucontrol->value.integer.value[i] != val) { + int dac; + ice->spec.phase28.master[i] &= ~WM_VOL_MUTE; + ice->spec.phase28.master[i] |= + ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; + for (dac = 0; dac < ice->num_total_dacs; dac += 2) + wm_set_vol(ice, WM_DAC_ATTEN + dac + i, + ice->spec.phase28.vol[dac + i], + ice->spec.phase28.master[i]); + change = 1; + } + } + snd_ice1712_restore_gpio_status(ice); + + return change; +} + +/* digital master volume */ +#define PCM_0dB 0xff +#define PCM_RES 128 /* -64dB */ +#define PCM_MIN (PCM_0dB - PCM_RES) +static int wm_pcm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; /* mute (-64dB) */ + uinfo->value.integer.max = PCM_RES; /* 0dB */ + return 0; +} + +static int wm_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + + down(&ice->gpio_mutex); + val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; + val = val > PCM_MIN ? (val - PCM_MIN) : 0; + ucontrol->value.integer.value[0] = val; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short ovol, nvol; + int change = 0; + + snd_ice1712_save_gpio_status(ice); + nvol = ucontrol->value.integer.value[0]; + nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff; + ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; + if (ovol != nvol) { + wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */ + wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */ + change = 1; + } + snd_ice1712_restore_gpio_status(ice); + return change; +} + +/* + */ +static int phase28_mono_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +/* + * Deemphasis + */ +#define phase28_deemp_info phase28_mono_bool_info + +static int phase28_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf; + return 0; +} + +static int phase28_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int temp, temp2; + temp2 = temp = wm_get(ice, WM_DAC_CTRL2); + if (ucontrol->value.integer.value[0]) + temp |= 0xf; + else + temp &= ~0xf; + if (temp != temp2) { + wm_put(ice, WM_DAC_CTRL2, temp); + return 1; + } + return 0; +} + +/* + * ADC Oversampling + */ +static int phase28_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[2] = { "128x", "64x" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int phase28_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8; + return 0; +} + +static int phase28_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int temp, temp2; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + temp2 = temp = wm_get(ice, WM_MASTER); + + if (ucontrol->value.enumerated.item[0]) + temp |= 0x8; + else + temp &= ~0x8; + + if (temp != temp2) { + wm_put(ice, WM_MASTER, temp); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t phase28_dac_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = wm_master_mute_info, + .get = wm_master_mute_get, + .put = wm_master_mute_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = wm_master_vol_info, + .get = wm_master_vol_get, + .put = wm_master_vol_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Playback Switch", + .info = wm_mute_info, + .get = wm_mute_get, + .put = wm_mute_put, + .private_value = (2 << 8) | 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Playback Volume", + .info = wm_vol_info, + .get = wm_vol_get, + .put = wm_vol_put, + .private_value = (2 << 8) | 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Rear Playback Switch", + .info = wm_mute_info, + .get = wm_mute_get, + .put = wm_mute_put, + .private_value = (2 << 8) | 2 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Rear Playback Volume", + .info = wm_vol_info, + .get = wm_vol_get, + .put = wm_vol_put, + .private_value = (2 << 8) | 2 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Center Playback Switch", + .info = wm_mute_info, + .get = wm_mute_get, + .put = wm_mute_put, + .private_value = (1 << 8) | 4 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Center Playback Volume", + .info = wm_vol_info, + .get = wm_vol_get, + .put = wm_vol_put, + .private_value = (1 << 8) | 4 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "LFE Playback Switch", + .info = wm_mute_info, + .get = wm_mute_get, + .put = wm_mute_put, + .private_value = (1 << 8) | 5 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "LFE Playback Volume", + .info = wm_vol_info, + .get = wm_vol_get, + .put = wm_vol_put, + .private_value = (1 << 8) | 5 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Side Playback Switch", + .info = wm_mute_info, + .get = wm_mute_get, + .put = wm_mute_put, + .private_value = (2 << 8) | 6 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Side Playback Volume", + .info = wm_vol_info, + .get = wm_vol_get, + .put = wm_vol_put, + .private_value = (2 << 8) | 6 + } +}; + +static snd_kcontrol_new_t wm_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = wm_pcm_mute_info, + .get = wm_pcm_mute_get, + .put = wm_pcm_mute_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = wm_pcm_vol_info, + .get = wm_pcm_vol_get, + .put = wm_pcm_vol_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Deemphasis Switch", + .info = phase28_deemp_info, + .get = phase28_deemp_get, + .put = phase28_deemp_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Oversampling", + .info = phase28_oversampling_info, + .get = phase28_oversampling_get, + .put = phase28_oversampling_put + } +}; + +static int __devinit phase28_add_controls(ice1712_t *ice) +{ + unsigned int i, counts; + int err; + + counts = ARRAY_SIZE(phase28_dac_controls); + for (i = 0; i < counts; i++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice)); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(wm_controls); i++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice)); + if (err < 0) + return err; + } + + return 0; +} + struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_PHASE22, @@ -134,5 +853,14 @@ struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = { .eeprom_size = sizeof(phase22_eeprom), .eeprom_data = phase22_eeprom, }, + { + .subvendor = VT1724_SUBDEVICE_PHASE28, + .name = "Terratec PHASE 28", + .model = "phase28", + .chip_init = phase28_init, + .build_controls = phase28_add_controls, + .eeprom_size = sizeof(phase28_eeprom), + .eeprom_data = phase28_eeprom, + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h index 6230cf1..13e841b 100644 --- a/sound/pci/ice1712/phase.h +++ b/sound/pci/ice1712/phase.h @@ -24,11 +24,28 @@ * */ -#define PHASE_DEVICE_DESC "{Terratec,Phase 22}," +#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\ + "{Terratec,Phase 28}," #define VT1724_SUBDEVICE_PHASE22 0x3b155011 +#define VT1724_SUBDEVICE_PHASE28 0x3b154911 /* entry point */ extern struct snd_ice1712_card_info snd_vt1724_phase_cards[]; +/* PHASE28 GPIO bits */ +#define PHASE28_SPI_MISO (1 << 21) +#define PHASE28_WM_RESET (1 << 20) +#define PHASE28_SPI_CLK (1 << 19) +#define PHASE28_SPI_MOSI (1 << 18) +#define PHASE28_WM_RW (1 << 17) +#define PHASE28_AC97_RESET (1 << 16) +#define PHASE28_DIGITAL_SEL1 (1 << 15) +#define PHASE28_HP_SEL (1 << 14) +#define PHASE28_WM_CS (1 << 12) +#define PHASE28_AC97_COMMIT (1 << 11) +#define PHASE28_AC97_ADDR (1 << 10) +#define PHASE28_AC97_DATA_LOW (1 << 9) +#define PHASE28_AC97_DATA_HIGH (1 << 8) +#define PHASE28_AC97_DATA_MASK 0xFF #endif /* __SOUND_PHASE */ diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 3bd9262..ab61e38 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -110,6 +110,15 @@ struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = { .eeprom_size = sizeof(k8x800_eeprom), .eeprom_data = k8x800_eeprom, }, + { + .subvendor = VT1720_SUBDEVICE_SN25P, + .name = "Shuttle SN25P", + /* identical with k8x800 */ + .chip_init = k8x800_init, + .build_controls = k8x800_add_controls, + .eeprom_size = sizeof(k8x800_eeprom), + .eeprom_data = k8x800_eeprom, + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h index f949eb8..0b1b0ee 100644 --- a/sound/pci/ice1712/vt1720_mobo.h +++ b/sound/pci/ice1712/vt1720_mobo.h @@ -27,12 +27,14 @@ #define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\ "{Chaintech,ZNF3-150},"\ "{Chaintech,ZNF3-250},"\ - "{Chaintech,9CJS}," + "{Chaintech,9CJS},"\ + "{Shuttle,SN25P}," #define VT1720_SUBDEVICE_K8X800 0xf217052c #define VT1720_SUBDEVICE_ZNF3_150 0x0f2741f6 #define VT1720_SUBDEVICE_ZNF3_250 0x0f2745f6 #define VT1720_SUBDEVICE_9CJS 0x0f272327 +#define VT1720_SUBDEVICE_SN25P 0x97123650 extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[]; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 8b33b12..cc16f95 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1725,229 +1725,235 @@ static struct ac97_pcm ac97_pcm_defs[] __devinitdata = { static struct ac97_quirk ac97_quirks[] __devinitdata = { { - .vendor = 0x0e11, - .device = 0x008a, + .subvendor = 0x0e11, + .subdevice = 0x008a, .name = "Compaq Evo W4000", /* AD1885 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x0e11, - .device = 0x00b8, + .subvendor = 0x0e11, + .subdevice = 0x00b8, .name = "Compaq Evo D510C", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x0e11, - .device = 0x0860, + .subvendor = 0x0e11, + .subdevice = 0x0860, .name = "HP/Compaq nx7010", .type = AC97_TUNE_MUTE_LED }, { - .vendor = 0x1014, - .device = 0x1f00, + .subvendor = 0x1014, + .subdevice = 0x1f00, .name = "MS-9128", .type = AC97_TUNE_ALC_JACK }, { - .vendor = 0x1028, - .device = 0x00d8, + .subvendor = 0x1028, + .subdevice = 0x00d8, .name = "Dell Precision 530", /* AD1885 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x010d, + .subvendor = 0x1028, + .subdevice = 0x010d, .name = "Dell", /* which model? AD1885 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x0126, + .subvendor = 0x1028, + .subdevice = 0x0126, .name = "Dell Optiplex GX260", /* AD1981A */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x012c, + .subvendor = 0x1028, + .subdevice = 0x012c, .name = "Dell Precision 650", /* AD1981A */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x012d, + .subvendor = 0x1028, + .subdevice = 0x012d, .name = "Dell Precision 450", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x0147, + .subvendor = 0x1028, + .subdevice = 0x0147, .name = "Dell", /* which model? AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1028, - .device = 0x0163, + .subvendor = 0x1028, + .subdevice = 0x0163, .name = "Dell Unknown", /* STAC9750/51 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x103c, - .device = 0x006d, + .subvendor = 0x103c, + .subdevice = 0x006d, .name = "HP zv5000", .type = AC97_TUNE_MUTE_LED /*AD1981B*/ }, { /* FIXME: which codec? */ - .vendor = 0x103c, - .device = 0x00c3, + .subvendor = 0x103c, + .subdevice = 0x00c3, .name = "HP xw6000", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x103c, - .device = 0x088c, + .subvendor = 0x103c, + .subdevice = 0x088c, .name = "HP nc8000", .type = AC97_TUNE_MUTE_LED }, { - .vendor = 0x103c, - .device = 0x0890, + .subvendor = 0x103c, + .subdevice = 0x0890, .name = "HP nc6000", .type = AC97_TUNE_MUTE_LED }, { - .vendor = 0x103c, - .device = 0x129d, + .subvendor = 0x103c, + .subdevice = 0x129d, .name = "HP xw8000", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x103c, - .device = 0x12f1, + .subvendor = 0x103c, + .subdevice = 0x12f1, .name = "HP xw8200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x103c, - .device = 0x12f2, + .subvendor = 0x103c, + .subdevice = 0x12f2, .name = "HP xw6200", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x103c, - .device = 0x3008, + .subvendor = 0x103c, + .subdevice = 0x3008, .name = "HP xw4200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x104d, - .device = 0x8197, + .subvendor = 0x104d, + .subdevice = 0x8197, .name = "Sony S1XP", .type = AC97_TUNE_INV_EAPD }, { - .vendor = 0x1043, - .device = 0x80f3, + .subvendor = 0x1043, + .subdevice = 0x80f3, .name = "ASUS ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, { - .vendor = 0x10cf, - .device = 0x11c3, + .subvendor = 0x10cf, + .subdevice = 0x11c3, .name = "Fujitsu-Siemens E4010", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x10cf, - .device = 0x1253, + .subvendor = 0x10cf, + .subdevice = 0x1225, + .name = "Fujitsu-Siemens T3010", + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x10cf, + .subdevice = 0x1253, .name = "Fujitsu S6210", /* STAC9750/51 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x10f1, - .device = 0x2665, + .subvendor = 0x10f1, + .subdevice = 0x2665, .name = "Fujitsu-Siemens Celsius", /* AD1981? */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x10f1, - .device = 0x2885, + .subvendor = 0x10f1, + .subdevice = 0x2885, .name = "AMD64 Mobo", /* ALC650 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x110a, - .device = 0x0056, + .subvendor = 0x110a, + .subdevice = 0x0056, .name = "Fujitsu-Siemens Scenic", /* AD1981? */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x11d4, - .device = 0x5375, + .subvendor = 0x11d4, + .subdevice = 0x5375, .name = "ADI AD1985 (discrete)", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1462, - .device = 0x5470, + .subvendor = 0x1462, + .subdevice = 0x5470, .name = "MSI P4 ATX 645 Ultra", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1734, - .device = 0x0088, + .subvendor = 0x1734, + .subdevice = 0x0088, .name = "Fujitsu-Siemens D1522", /* AD1981 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x8086, - .device = 0x2000, + .subvendor = 0x8086, + .subdevice = 0x2000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, { - .vendor = 0x8086, - .device = 0x4000, + .subvendor = 0x8086, + .subdevice = 0x4000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, { - .vendor = 0x8086, - .device = 0x4856, + .subvendor = 0x8086, + .subdevice = 0x4856, .name = "Intel D845WN (82801BA)", .type = AC97_TUNE_SWAP_HP }, { - .vendor = 0x8086, - .device = 0x4d44, + .subvendor = 0x8086, + .subdevice = 0x4d44, .name = "Intel D850EMV2", /* AD1885 */ .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x8086, - .device = 0x4d56, + .subvendor = 0x8086, + .subdevice = 0x4d56, .name = "Intel ICH/AD1885", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x8086, - .device = 0x6000, + .subvendor = 0x8086, + .subdevice = 0x6000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, { - .vendor = 0x8086, - .device = 0xe000, + .subvendor = 0x8086, + .subdevice = 0xe000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, #if 0 /* FIXME: this seems wrong on most boards */ { - .vendor = 0x8086, - .device = 0xa000, + .subvendor = 0x8086, + .subdevice = 0xa000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", .type = AC97_TUNE_HP_ONLY @@ -2849,7 +2855,7 @@ static struct pci_driver driver = { static int __init alsa_card_intel8x0_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_intel8x0_exit(void) diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 67da096..bb758c7 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -35,7 +35,6 @@ #include <sound/pcm.h> #include <sound/ac97_codec.h> #include <sound/info.h> -#include <sound/control.h> #include <sound/initval.h> MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); @@ -292,60 +291,9 @@ static struct pci_device_id snd_intel8x0m_ids[] = { #endif { 0, } }; -static int snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol); -static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol); -static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol, - snd_ctl_elem_info_t *uinfo); - -#define PRIVATE_VALUE_INITIALIZER(r,m) (((r) & 0xffff) << 16 | ((m) & 0xffff)) -#define PRIVATE_VALUE_MASK(control) ((control)->private_value & 0xffff) -#define PRIVATE_VALUE_REG(control) (((control)->private_value >> 16) & 0xffff) - -static snd_kcontrol_new_t snd_intel8x0m_mixer_switches[] __devinitdata = { - { .name = "Off-hook Switch", - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = snd_intel8x0m_switch_default_info, - .get = snd_intel8x0m_switch_default_get, - .put = snd_intel8x0m_switch_default_put, - .private_value = PRIVATE_VALUE_INITIALIZER(AC97_GPIO_STATUS,AC97_GPIO_LINE1_OH) - } -}; MODULE_DEVICE_TABLE(pci, snd_intel8x0m_ids); -static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol, - snd_ctl_elem_info_t *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol) -{ - unsigned short mask = PRIVATE_VALUE_MASK(kcontrol); - unsigned short reg = PRIVATE_VALUE_REG(kcontrol); - intel8x0_t *chip = snd_kcontrol_chip(kcontrol); - unsigned int status; - status = snd_ac97_read(chip->ac97, reg) & mask ? 1 : 0; - ucontrol->value.integer.value[0] = status; - return 0; -} -static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol) -{ - unsigned short mask = PRIVATE_VALUE_MASK(kcontrol); - unsigned short reg = PRIVATE_VALUE_REG(kcontrol); - intel8x0_t *chip = snd_kcontrol_chip(kcontrol); - unsigned short new_status = ucontrol->value.integer.value[0] ? mask : ~mask; - return snd_ac97_update_bits(chip->ac97, reg, - mask, new_status); -} /* * Lowlevel I/O - busmaster */ @@ -500,6 +448,8 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, res = 0xffff; } } + if (reg == AC97_GPIO_STATUS) + iagetword(chip, 0); /* clear semaphore */ return res; } @@ -698,21 +648,6 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substrea return bytes_to_frames(substream->runtime, ptr); } -static int snd_intel8x0m_pcm_trigger(snd_pcm_substream_t *substream, int cmd) -{ - /* hook off/on on start/stop */ - /* Moved this to mixer control */ - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - break; - case SNDRV_PCM_TRIGGER_STOP: - break; - default: - return -EINVAL; - } - return snd_intel8x0_pcm_trigger(substream,cmd); -} - static int snd_intel8x0m_pcm_prepare(snd_pcm_substream_t * substream) { intel8x0_t *chip = snd_pcm_substream_chip(substream); @@ -808,7 +743,7 @@ static snd_pcm_ops_t snd_intel8x0m_playback_ops = { .hw_params = snd_intel8x0_hw_params, .hw_free = snd_intel8x0_hw_free, .prepare = snd_intel8x0m_pcm_prepare, - .trigger = snd_intel8x0m_pcm_trigger, + .trigger = snd_intel8x0_pcm_trigger, .pointer = snd_intel8x0_pcm_pointer, }; @@ -819,7 +754,7 @@ static snd_pcm_ops_t snd_intel8x0m_capture_ops = { .hw_params = snd_intel8x0_hw_params, .hw_free = snd_intel8x0_hw_free, .prepare = snd_intel8x0m_pcm_prepare, - .trigger = snd_intel8x0m_pcm_trigger, + .trigger = snd_intel8x0_pcm_trigger, .pointer = snd_intel8x0_pcm_pointer, }; @@ -947,7 +882,6 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) ac97_t *x97; int err; unsigned int glob_sta = 0; - unsigned int idx; static ac97_bus_ops_t ops = { .write = snd_intel8x0_codec_write, .read = snd_intel8x0_codec_read, @@ -983,10 +917,6 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) chip->ichd[ICHD_MDMIN].ac97 = x97; chip->ichd[ICHD_MDMOUT].ac97 = x97; } - for (idx = 0; idx < ARRAY_SIZE(snd_intel8x0m_mixer_switches); idx++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_intel8x0m_mixer_switches[idx], chip))) < 0) - goto __err; - } chip->in_ac97_init = 0; return 0; @@ -1450,7 +1380,7 @@ static struct pci_driver driver = { static int __init alsa_card_intel8x0m_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_intel8x0m_exit(void) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index bb1de20..79d8eda 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2541,7 +2541,7 @@ static struct pci_driver driver = { static int __init alsa_card_korg1212_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_korg1212_exit(void) diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 2cf3308..096f151 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -779,6 +779,12 @@ struct m3_quirk { (e.g. for IrDA on Dell Inspirons) */ }; +struct m3_hv_quirk { + u16 vendor, device, subsystem_vendor, subsystem_device; + u32 config; /* ALLEGRO_CONFIG hardware volume bits */ + int is_omnibook; /* Do HP OmniBook GPIO magic? */ +}; + struct m3_list { int curlen; int mem_addr; @@ -828,6 +834,7 @@ struct snd_m3 { struct pci_dev *pci; struct m3_quirk *quirk; + struct m3_hv_quirk *hv_quirk; int dacs_active; int timer_users; @@ -851,6 +858,11 @@ struct snd_m3 { m3_dma_t *substreams; spinlock_t reg_lock; + spinlock_t ac97_lock; + + snd_kcontrol_t *master_switch; + snd_kcontrol_t *master_volume; + struct tasklet_struct hwvol_tq; #ifdef CONFIG_PM u16 *suspend_mem; @@ -968,6 +980,71 @@ static struct m3_quirk m3_quirk_list[] = { { NULL } }; +/* These values came from the Windows driver. */ +static struct m3_hv_quirk m3_hv_quirk_list[] = { + /* Allegro chips */ + { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, + { 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */ + { 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 }, + /* Maestro3 chips */ + { 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */ + { 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */ + { 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 }, + { 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, + { 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, + { 0 } +}; /* * lowlevel functions @@ -1565,6 +1642,68 @@ static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s) } } +static void snd_m3_update_hw_volume(unsigned long private_data) +{ + m3_t *chip = (m3_t *) private_data; + int x, val; + unsigned long flags; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee; + + /* Reset the volume control registers. */ + outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE); + outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE); + outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER); + outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER); + + if (!chip->master_switch || !chip->master_volume) + return; + + /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ + spin_lock_irqsave(&chip->ac97_lock, flags); + + val = chip->ac97->regs[AC97_MASTER_VOL]; + switch (x) { + case 0x88: + /* mute */ + val ^= 0x8000; + chip->ac97->regs[AC97_MASTER_VOL] = val; + outw(val, chip->iobase + CODEC_DATA); + outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_switch->id); + break; + case 0xaa: + /* volume up */ + if ((val & 0x7f) > 0) + val--; + if ((val & 0x7f00) > 0) + val -= 0x0100; + chip->ac97->regs[AC97_MASTER_VOL] = val; + outw(val, chip->iobase + CODEC_DATA); + outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + break; + case 0x66: + /* volume down */ + if ((val & 0x7f) < 0x1f) + val++; + if ((val & 0x7f00) < 0x1f00) + val += 0x0100; + chip->ac97->regs[AC97_MASTER_VOL] = val; + outw(val, chip->iobase + CODEC_DATA); + outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + break; + } + spin_unlock_irqrestore(&chip->ac97_lock, flags); +} + static irqreturn_t snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { @@ -1576,7 +1715,10 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (status == 0xff) return IRQ_NONE; - + + if (status & HV_INT_PENDING) + tasklet_hi_schedule(&chip->hwvol_tq); + /* * ack an assp int if its running * and has an int pending @@ -1605,7 +1747,7 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif /* ack ints */ - snd_m3_outw(chip, HOST_INT_STATUS, status); + outb(status, chip->iobase + HOST_INT_STATUS); return IRQ_HANDLED; } @@ -1842,24 +1984,32 @@ static unsigned short snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) { m3_t *chip = ac97->private_data; + unsigned long flags; + unsigned short data; if (snd_m3_ac97_wait(chip)) return 0xffff; + spin_lock_irqsave(&chip->ac97_lock, flags); snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); if (snd_m3_ac97_wait(chip)) return 0xffff; - return snd_m3_inw(chip, CODEC_DATA); + data = snd_m3_inw(chip, CODEC_DATA); + spin_unlock_irqrestore(&chip->ac97_lock, flags); + return data; } static void snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { m3_t *chip = ac97->private_data; + unsigned long flags; if (snd_m3_ac97_wait(chip)) return; + spin_lock_irqsave(&chip->ac97_lock, flags); snd_m3_outw(chip, val, CODEC_DATA); snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); + spin_unlock_irqrestore(&chip->ac97_lock, flags); } @@ -1968,6 +2118,7 @@ static int __devinit snd_m3_mixer(m3_t *chip) { ac97_bus_t *pbus; ac97_template_t ac97; + snd_ctl_elem_id_t id; int err; static ac97_bus_ops_t ops = { .write = snd_m3_ac97_write, @@ -1988,6 +2139,15 @@ static int __devinit snd_m3_mixer(m3_t *chip) schedule_timeout(HZ / 10); snd_ac97_write(chip->ac97, AC97_PCM, 0); + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &id); + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &id); + return 0; } @@ -2293,6 +2453,7 @@ static int snd_m3_chip_init(m3_t *chip) { struct pci_dev *pcidev = chip->pci; + unsigned long io = chip->iobase; u32 n; u16 w; u8 t; /* makes as much sense as 'n', no? */ @@ -2303,8 +2464,27 @@ snd_m3_chip_init(m3_t *chip) DISABLE_LEGACY); pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w); + if (chip->hv_quirk && chip->hv_quirk->is_omnibook) { + /* + * Volume buttons on some HP OmniBook laptops don't work + * correctly. This makes them work for the most part. + * + * Volume up and down buttons on the laptop side work. + * Fn+cursor_up (volme up) works. + * Fn+cursor_down (volume down) doesn't work. + * Fn+F7 (mute) works acts as volume up. + */ + outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK); + outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION); + outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA); + outw(0xffff, io + GPIO_MASK); + } pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); - n &= REDUCED_DEBOUNCE; + n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD); + if (chip->hv_quirk) + n |= chip->hv_quirk->config; + /* For some reason we must always use reduced debounce. */ + n |= REDUCED_DEBOUNCE; n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); @@ -2332,6 +2512,12 @@ snd_m3_chip_init(m3_t *chip) outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); + outb(0x00, io + HARDWARE_VOL_CTRL); + outb(0x88, io + SHADOW_MIX_REG_VOICE); + outb(0x88, io + HW_VOL_COUNTER_VOICE); + outb(0x88, io + SHADOW_MIX_REG_MASTER); + outb(0x88, io + HW_VOL_COUNTER_MASTER); + return 0; } @@ -2341,7 +2527,7 @@ snd_m3_enable_ints(m3_t *chip) unsigned long io = chip->iobase; /* TODO: MPU401 not supported yet */ - outw(ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL); + outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL); outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, io + ASSP_CONTROL_C); } @@ -2367,7 +2553,7 @@ static int snd_m3_free(m3_t *chip) kfree(chip->substreams); } if (chip->iobase) { - snd_m3_outw(chip, HOST_INT_CTRL, 0); /* disable ints */ + outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */ } #ifdef CONFIG_PM @@ -2486,7 +2672,7 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, m3_t *chip; int i, err; struct m3_quirk *quirk; - u16 subsystem_vendor, subsystem_device; + struct m3_hv_quirk *hv_quirk; static snd_device_ops_t ops = { .dev_free = snd_m3_dev_free, }; @@ -2524,18 +2710,25 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, chip->pci = pci; chip->irq = -1; - pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); - for (quirk = m3_quirk_list; quirk->vendor; quirk++) { - if (subsystem_vendor == quirk->vendor && - subsystem_device == quirk->device) { + if (pci->subsystem_vendor == quirk->vendor && + pci->subsystem_device == quirk->device) { printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name); chip->quirk = quirk; break; } } + for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) { + if (pci->vendor == hv_quirk->vendor && + pci->device == hv_quirk->device && + pci->subsystem_vendor == hv_quirk->subsystem_vendor && + pci->subsystem_device == hv_quirk->subsystem_device) { + chip->hv_quirk = hv_quirk; + break; + } + } + chip->external_amp = enable_amp; if (amp_gpio >= 0 && amp_gpio <= 0x0f) chip->amp_gpio = amp_gpio; @@ -2593,6 +2786,9 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, return err; } + spin_lock_init(&chip->ac97_lock); + tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); + if ((err = snd_m3_mixer(chip)) < 0) return err; @@ -2702,7 +2898,7 @@ static struct pci_driver driver = { static int __init alsa_card_m3_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_m3_exit(void) diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 65bb0f4..082c0d0 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1431,7 +1431,7 @@ static struct pci_driver driver = { static int __init alsa_card_mixart_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_mixart_exit(void) diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 356fbea..8a52091 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1645,7 +1645,7 @@ static struct pci_driver driver = { static int __init alsa_card_nm256_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_nm256_exit(void) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index b96acd5..b7b554d 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -2031,7 +2031,7 @@ static struct pci_driver driver = { static int __init alsa_card_rme32_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_rme32_exit(void) diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 8e26668..10c4f45 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -2437,7 +2437,7 @@ static struct pci_driver driver = { static int __init alsa_card_rme96_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_rme96_exit(void) diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile index 917374c..d2c294e 100644 --- a/sound/pci/rme9652/Makefile +++ b/sound/pci/rme9652/Makefile @@ -5,7 +5,9 @@ snd-rme9652-objs := rme9652.o snd-hdsp-objs := hdsp.o +snd-hdspm-objs := hdspm.o # Toplevel Module Dependency obj-$(CONFIG_SND_RME9652) += snd-rme9652.o obj-$(CONFIG_SND_HDSP) += snd-hdsp.o +obj-$(CONFIG_SND_HDSPM) +=snd-hdspm.o diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 12efbf0..a673cc4 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -559,18 +559,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer { dmab->dev.type = SNDRV_DMA_TYPE_DEV; dmab->dev.dev = snd_dma_pci_data(pci); - if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), - size, dmab) < 0) - return -ENOMEM; + if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { + if (dmab->bytes >= size) + return 0; } + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, dmab) < 0) + return -ENOMEM; return 0; } static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci) { - if (dmab->area) + if (dmab->area) { + dmab->dev.dev = NULL; /* make it anonymous */ snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci)); + } } @@ -4912,19 +4916,9 @@ static int __devinit hdsp_request_fw_loader(hdsp_t *hdsp) release_firmware(fw); return -EINVAL; } -#ifdef SNDRV_BIG_ENDIAN - { - int i; - u32 *src = (u32*)fw->data; - for (i = 0; i < ARRAY_SIZE(hdsp->firmware_cache); i++, src++) - hdsp->firmware_cache[i] = ((*src & 0x000000ff) << 16) | - ((*src & 0x0000ff00) << 8) | - ((*src & 0x00ff0000) >> 8) | - ((*src & 0xff000000) >> 16); - } -#else + memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache)); -#endif + release_firmware(fw); hdsp->state |= HDSP_FirmwareCached; @@ -5194,7 +5188,7 @@ static struct pci_driver driver = { static int __init alsa_card_hdsp_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_hdsp_exit(void) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c new file mode 100644 index 0000000..9e86d0e --- /dev/null +++ b/sound/pci/rme9652/hdspm.c @@ -0,0 +1,3671 @@ +/* -*- linux-c -*- + * + * ALSA driver for RME Hammerfall DSP MADI audio interface(s) + * + * Copyright (c) 2003 Winfried Ritsch (IEM) + * code based on hdsp.c Paul Davis + * Marcus Andersson + * Thomas Charbonnel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <asm/io.h> + +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/info.h> +#include <sound/asoundef.h> +#include <sound/rawmidi.h> +#include <sound/hwdep.h> +#include <sound/initval.h> + +#include <sound/hdspm.h> + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ + +/* Disable precise pointer at start */ +static int precise_ptr[SNDRV_CARDS]; + +/* Send all playback to line outs */ +static int line_outs_monitor[SNDRV_CARDS]; + +/* Enable Analog Outs on Channel 63/64 by default */ +static int enable_monitor[SNDRV_CARDS]; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for RME HDSPM interface."); + +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for RME HDSPM interface."); + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards."); + +module_param_array(precise_ptr, bool, NULL, 0444); +MODULE_PARM_DESC(precise_ptr, "Enable precise pointer, or disable."); + +module_param_array(line_outs_monitor, bool, NULL, 0444); +MODULE_PARM_DESC(line_outs_monitor, + "Send playback streams to analog outs by default."); + +module_param_array(enable_monitor, bool, NULL, 0444); +MODULE_PARM_DESC(enable_monitor, + "Enable Analog Out on Channel 63/64 by default."); + +MODULE_AUTHOR + ("Winfried Ritsch <ritsch_AT_iem.at>, Paul Davis <paul@linuxaudiosystems.com>, " + "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>"); +MODULE_DESCRIPTION("RME HDSPM"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); + +/* --- Write registers. --- + These are defined as byte-offsets from the iobase value. */ + +#define HDSPM_controlRegister 64 +#define HDSPM_interruptConfirmation 96 +#define HDSPM_control2Reg 256 /* not in specs ???????? */ +#define HDSPM_midiDataOut0 352 /* just believe in old code */ +#define HDSPM_midiDataOut1 356 + +/* DMA enable for 64 channels, only Bit 0 is relevant */ +#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ +#define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */ + +/* 16 page addresses for each of the 64 channels DMA buffer in and out + (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */ +#define HDSPM_pageAddressBufferOut 8192 +#define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4) + +#define HDSPM_MADI_mixerBase 32768 /* 32768-65535 for 2x64x64 Fader */ + +#define HDSPM_MATRIX_MIXER_SIZE 8192 /* = 2*64*64 * 4 Byte => 32kB */ + +/* --- Read registers. --- + These are defined as byte-offsets from the iobase value */ +#define HDSPM_statusRegister 0 +#define HDSPM_statusRegister2 96 + +#define HDSPM_midiDataIn0 360 +#define HDSPM_midiDataIn1 364 + +/* status is data bytes in MIDI-FIFO (0-128) */ +#define HDSPM_midiStatusOut0 384 +#define HDSPM_midiStatusOut1 388 +#define HDSPM_midiStatusIn0 392 +#define HDSPM_midiStatusIn1 396 + + +/* the meters are regular i/o-mapped registers, but offset + considerably from the rest. the peak registers are reset + when read; the least-significant 4 bits are full-scale counters; + the actual peak value is in the most-significant 24 bits. +*/ +#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */ + +/* --- Control Register bits --------- */ +#define HDSPM_Start (1<<0) /* start engine */ + +#define HDSPM_Latency0 (1<<1) /* buffer size = 2^n */ +#define HDSPM_Latency1 (1<<2) /* where n is defined */ +#define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */ + +#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */ + +#define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */ + +#define HDSPM_Frequency0 (1<<6) /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */ +#define HDSPM_Frequency1 (1<<7) /* 0=32kHz/64kHz */ +#define HDSPM_DoubleSpeed (1<<8) /* 0=normal speed, 1=double speed */ +#define HDSPM_QuadSpeed (1<<31) /* quad speed bit, not implemented now */ + +#define HDSPM_TX_64ch (1<<10) /* Output 64channel MODE=1, + 56channelMODE=0 */ + +#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, + 0=off, 1=on */ + +#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ +#define HDSPM_InputSelect1 (1<<15) /* should be 0 */ + +#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ +#define HDSPM_SyncRef1 (1<<17) /* should be 0 */ + +#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use + AES additional bits in + lower 5 Audiodatabits ??? */ + +#define HDSPM_Midi0InterruptEnable (1<<22) +#define HDSPM_Midi1InterruptEnable (1<<23) + +#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */ + + +/* --- bit helper defines */ +#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2) +#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1) +#define HDSPM_InputMask (HDSPM_InputSelect0|HDSPM_InputSelect1) +#define HDSPM_InputOptical 0 +#define HDSPM_InputCoaxial (HDSPM_InputSelect0) +#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1) +#define HDSPM_SyncRef_Word 0 +#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) + +#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ +#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ + +#define HDSPM_Frequency32KHz HDSPM_Frequency0 +#define HDSPM_Frequency44_1KHz HDSPM_Frequency1 +#define HDSPM_Frequency48KHz (HDSPM_Frequency1|HDSPM_Frequency0) +#define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0) +#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1) +#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0) + +/* --- for internal discrimination */ +#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ +#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1 +#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2 +#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3 +#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4 +#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5 +#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6 +#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7 +#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8 +#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9 + +/* Synccheck Status */ +#define HDSPM_SYNC_CHECK_NO_LOCK 0 +#define HDSPM_SYNC_CHECK_LOCK 1 +#define HDSPM_SYNC_CHECK_SYNC 2 + +/* AutoSync References - used by "autosync_ref" control switch */ +#define HDSPM_AUTOSYNC_FROM_WORD 0 +#define HDSPM_AUTOSYNC_FROM_MADI 1 +#define HDSPM_AUTOSYNC_FROM_NONE 2 + +/* Possible sources of MADI input */ +#define HDSPM_OPTICAL 0 /* optical */ +#define HDSPM_COAXIAL 1 /* BNC */ + +#define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask) +#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1) + +#define hdspm_encode_in(x) (((x)&0x3)<<14) +#define hdspm_decode_in(x) (((x)>>14)&0x3) + +/* --- control2 register bits --- */ +#define HDSPM_TMS (1<<0) +#define HDSPM_TCK (1<<1) +#define HDSPM_TDI (1<<2) +#define HDSPM_JTAG (1<<3) +#define HDSPM_PWDN (1<<4) +#define HDSPM_PROGRAM (1<<5) +#define HDSPM_CONFIG_MODE_0 (1<<6) +#define HDSPM_CONFIG_MODE_1 (1<<7) +/*#define HDSPM_VERSION_BIT (1<<8) not defined any more*/ +#define HDSPM_BIGENDIAN_MODE (1<<9) +#define HDSPM_RD_MULTIPLE (1<<10) + +/* --- Status Register bits --- */ +#define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */ +#define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn. MODE=0 */ +#define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 (like inp0) */ +#define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ + +#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ + /* since 64byte accurate last 6 bits + are not used */ + +#define HDSPM_madiSync (1<<18) /* MADI is in sync */ +#define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */ + +#define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */ +#define HDSPM_madiFreq1 (1<<23) /* 1=32, 2=44.1 3=48 */ +#define HDSPM_madiFreq2 (1<<24) /* 4=64, 5=88.2 6=96 */ +#define HDSPM_madiFreq3 (1<<25) /* 7=128, 8=176.4 9=192 */ + +#define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with Interrupt */ +#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */ +#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */ + +/* --- status bit helpers */ +#define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2|HDSPM_madiFreq3) +#define HDSPM_madiFreq32 (HDSPM_madiFreq0) +#define HDSPM_madiFreq44_1 (HDSPM_madiFreq1) +#define HDSPM_madiFreq48 (HDSPM_madiFreq0|HDSPM_madiFreq1) +#define HDSPM_madiFreq64 (HDSPM_madiFreq2) +#define HDSPM_madiFreq88_2 (HDSPM_madiFreq0|HDSPM_madiFreq2) +#define HDSPM_madiFreq96 (HDSPM_madiFreq1|HDSPM_madiFreq2) +#define HDSPM_madiFreq128 (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2) +#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3) +#define HDSPM_madiFreq192 (HDSPM_madiFreq3|HDSPM_madiFreq0) + +/* Status2 Register bits */ + +#define HDSPM_version0 (1<<0) /* not realy defined but I guess */ +#define HDSPM_version1 (1<<1) /* in former cards it was ??? */ +#define HDSPM_version2 (1<<2) + +#define HDSPM_wcLock (1<<3) /* Wordclock is detected and locked */ +#define HDSPM_wcSync (1<<4) /* Wordclock is in sync with systemclock */ + +#define HDSPM_wc_freq0 (1<<5) /* input freq detected via autosync */ +#define HDSPM_wc_freq1 (1<<6) /* 001=32, 010==44.1, 011=48, */ +#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */ +/* missing Bit for 111=128, 1000=176.4, 1001=192 */ + +#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */ +#define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */ +#define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */ + +#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync) + +#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2) +#define HDSPM_wcFreq32 (HDSPM_wc_freq0) +#define HDSPM_wcFreq44_1 (HDSPM_wc_freq1) +#define HDSPM_wcFreq48 (HDSPM_wc_freq0|HDSPM_wc_freq1) +#define HDSPM_wcFreq64 (HDSPM_wc_freq2) +#define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2) +#define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2) + + +#define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2) +#define HDSPM_SelSyncRef_WORD 0 +#define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) +#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2) + +/* Mixer Values */ +#define UNITY_GAIN 32768 /* = 65536/2 */ +#define MINUS_INFINITY_GAIN 0 + +/* PCI info */ +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 +#endif + + +/* Number of channels for different Speed Modes */ +#define MADI_SS_CHANNELS 64 +#define MADI_DS_CHANNELS 32 +#define MADI_QS_CHANNELS 16 + +/* the size of a substream (1 mono data stream) */ +#define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024) +#define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES) + +/* the size of the area we need to allocate for DMA transfers. the + size is the same regardless of the number of channels, and + also the latency to use. + for one direction !!! +*/ +#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) +#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024) + +typedef struct _hdspm hdspm_t; +typedef struct _hdspm_midi hdspm_midi_t; + +struct _hdspm_midi { + hdspm_t *hdspm; + int id; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; + char istimer; /* timer in use */ + struct timer_list timer; + spinlock_t lock; + int pending; +}; + +struct _hdspm { + spinlock_t lock; + snd_pcm_substream_t *capture_substream; /* only one playback */ + snd_pcm_substream_t *playback_substream; /* and/or capture stream */ + + char *card_name; /* for procinfo */ + unsigned short firmware_rev; /* dont know if relevant */ + + int precise_ptr; /* use precise pointers, to be tested */ + int monitor_outs; /* set up monitoring outs init flag */ + + u32 control_register; /* cached value */ + u32 control2_register; /* cached value */ + + hdspm_midi_t midi[2]; + struct tasklet_struct midi_tasklet; + + size_t period_bytes; + unsigned char ss_channels; /* channels of card in single speed */ + unsigned char ds_channels; /* Double Speed */ + unsigned char qs_channels; /* Quad Speed */ + + unsigned char *playback_buffer; /* suitably aligned address */ + unsigned char *capture_buffer; /* suitably aligned address */ + + pid_t capture_pid; /* process id which uses capture */ + pid_t playback_pid; /* process id which uses capture */ + int running; /* running status */ + + int last_external_sample_rate; /* samplerate mystic ... */ + int last_internal_sample_rate; + int system_sample_rate; + + char *channel_map; /* channel map for DS and Quadspeed */ + + int dev; /* Hardware vars... */ + int irq; + unsigned long port; + void __iomem *iobase; + + int irq_count; /* for debug */ + + snd_card_t *card; /* one card */ + snd_pcm_t *pcm; /* has one pcm */ + snd_hwdep_t *hwdep; /* and a hwdep for additional ioctl */ + struct pci_dev *pci; /* and an pci info */ + + /* Mixer vars */ + snd_kcontrol_t *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* fast alsa mixer */ + snd_kcontrol_t *input_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */ + hdspm_mixer_t *mixer; /* full mixer accessable over mixer ioctl or hwdep-device */ + +}; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refer to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +static char channel_map_madi_ds[HDSPM_MAX_CHANNELS] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 60, 62, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_madi_qs[HDSPM_MAX_CHANNELS] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +static struct pci_device_id snd_hdspm_ids[] = { + { + .vendor = PCI_VENDOR_ID_XILINX, + .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0, + .class_mask = 0, + .driver_data = 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, snd_hdspm_ids); + +/* prototypes */ +static int __devinit snd_hdspm_create_alsa_devices(snd_card_t * card, + hdspm_t * hdspm); +static int __devinit snd_hdspm_create_pcm(snd_card_t * card, + hdspm_t * hdspm); + +static inline void snd_hdspm_initialize_midi_flush(hdspm_t * hdspm); +static int hdspm_update_simple_mixer_controls(hdspm_t * hdspm); +static int hdspm_autosync_ref(hdspm_t * hdspm); +static int snd_hdspm_set_defaults(hdspm_t * hdspm); +static void hdspm_set_sgbuf(hdspm_t * hdspm, struct snd_sg_buf *sgbuf, + unsigned int reg, int channels); + +/* Write/read to/from HDSPM with Adresses in Bytes + not words but only 32Bit writes are allowed */ + +static inline void hdspm_write(hdspm_t * hdspm, unsigned int reg, + unsigned int val) +{ + writel(val, hdspm->iobase + reg); +} + +static inline unsigned int hdspm_read(hdspm_t * hdspm, unsigned int reg) +{ + return readl(hdspm->iobase + reg); +} + +/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader + mixer is write only on hardware so we have to cache him for read + each fader is a u32, but uses only the first 16 bit */ + +static inline int hdspm_read_in_gain(hdspm_t * hdspm, unsigned int chan, + unsigned int in) +{ + if (chan > HDSPM_MIXER_CHANNELS || in > HDSPM_MIXER_CHANNELS) + return 0; + + return hdspm->mixer->ch[chan].in[in]; +} + +static inline int hdspm_read_pb_gain(hdspm_t * hdspm, unsigned int chan, + unsigned int pb) +{ + if (chan > HDSPM_MIXER_CHANNELS || pb > HDSPM_MIXER_CHANNELS) + return 0; + return hdspm->mixer->ch[chan].pb[pb]; +} + +static inline int hdspm_write_in_gain(hdspm_t * hdspm, unsigned int chan, + unsigned int in, unsigned short data) +{ + if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS) + return -1; + + hdspm_write(hdspm, + HDSPM_MADI_mixerBase + + ((in + 128 * chan) * sizeof(u32)), + (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF)); + return 0; +} + +static inline int hdspm_write_pb_gain(hdspm_t * hdspm, unsigned int chan, + unsigned int pb, unsigned short data) +{ + if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS) + return -1; + + hdspm_write(hdspm, + HDSPM_MADI_mixerBase + + ((64 + pb + 128 * chan) * sizeof(u32)), + (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF)); + return 0; +} + + +/* enable DMA for specific channels, now available for DSP-MADI */ +static inline void snd_hdspm_enable_in(hdspm_t * hdspm, int i, int v) +{ + hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v); +} + +static inline void snd_hdspm_enable_out(hdspm_t * hdspm, int i, int v) +{ + hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v); +} + +/* check if same process is writing and reading */ +static inline int snd_hdspm_use_is_exclusive(hdspm_t * hdspm) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&hdspm->lock, flags); + if ((hdspm->playback_pid != hdspm->capture_pid) && + (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) { + ret = 0; + } + spin_unlock_irqrestore(&hdspm->lock, flags); + return ret; +} + +/* check for external sample rate */ +static inline int hdspm_external_sample_rate(hdspm_t * hdspm) +{ + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); + unsigned int rate_bits; + int rate = 0; + + /* if wordclock has synced freq and wordclock is valid */ + if ((status2 & HDSPM_wcLock) != 0 && + (status & HDSPM_SelSyncRef0) == 0) { + + rate_bits = status2 & HDSPM_wcFreqMask; + + switch (rate_bits) { + case HDSPM_wcFreq32: + rate = 32000; + break; + case HDSPM_wcFreq44_1: + rate = 44100; + break; + case HDSPM_wcFreq48: + rate = 48000; + break; + case HDSPM_wcFreq64: + rate = 64000; + break; + case HDSPM_wcFreq88_2: + rate = 88200; + break; + case HDSPM_wcFreq96: + rate = 96000; + break; + /* Quadspeed Bit missing ???? */ + default: + rate = 0; + break; + } + } + + /* if rate detected and Syncref is Word than have it, word has priority to MADI */ + if (rate != 0 + && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + return rate; + + /* maby a madi input (which is taken if sel sync is madi) */ + if (status & HDSPM_madiLock) { + rate_bits = status & HDSPM_madiFreqMask; + + switch (rate_bits) { + case HDSPM_madiFreq32: + rate = 32000; + break; + case HDSPM_madiFreq44_1: + rate = 44100; + break; + case HDSPM_madiFreq48: + rate = 48000; + break; + case HDSPM_madiFreq64: + rate = 64000; + break; + case HDSPM_madiFreq88_2: + rate = 88200; + break; + case HDSPM_madiFreq96: + rate = 96000; + break; + case HDSPM_madiFreq128: + rate = 128000; + break; + case HDSPM_madiFreq176_4: + rate = 176400; + break; + case HDSPM_madiFreq192: + rate = 192000; + break; + default: + rate = 0; + break; + } + } + return rate; +} + +/* Latency function */ +static inline void hdspm_compute_period_size(hdspm_t * hdspm) +{ + hdspm->period_bytes = + 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); +} + +static snd_pcm_uframes_t hdspm_hw_pointer(hdspm_t * hdspm) +{ + int position; + + position = hdspm_read(hdspm, HDSPM_statusRegister); + + if (!hdspm->precise_ptr) { + return (position & HDSPM_BufferID) ? (hdspm->period_bytes / + 4) : 0; + } + + /* hwpointer comes in bytes and is 64Bytes accurate (by docu since PCI Burst) + i have experimented that it is at most 64 Byte to much for playing + so substraction of 64 byte should be ok for ALSA, but use it only + for application where you know what you do since if you come to + near with record pointer it can be a disaster */ + + position &= HDSPM_BufferPositionMask; + position = ((position - 64) % (2 * hdspm->period_bytes)) / 4; + + return position; +} + + +static inline void hdspm_start_audio(hdspm_t * s) +{ + s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start); + hdspm_write(s, HDSPM_controlRegister, s->control_register); +} + +static inline void hdspm_stop_audio(hdspm_t * s) +{ + s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable); + hdspm_write(s, HDSPM_controlRegister, s->control_register); +} + +/* should I silence all or only opened ones ? doit all for first even is 4MB*/ +static inline void hdspm_silence_playback(hdspm_t * hdspm) +{ + int i; + int n = hdspm->period_bytes; + void *buf = hdspm->playback_buffer; + + snd_assert(buf != NULL, return); + + for (i = 0; i < HDSPM_MAX_CHANNELS; i++) { + memset(buf, 0, n); + buf += HDSPM_CHANNEL_BUFFER_BYTES; + } +} + +static int hdspm_set_interrupt_interval(hdspm_t * s, unsigned int frames) +{ + int n; + + spin_lock_irq(&s->lock); + + frames >>= 7; + n = 0; + while (frames) { + n++; + frames >>= 1; + } + s->control_register &= ~HDSPM_LatencyMask; + s->control_register |= hdspm_encode_latency(n); + + hdspm_write(s, HDSPM_controlRegister, s->control_register); + + hdspm_compute_period_size(s); + + spin_unlock_irq(&s->lock); + + return 0; +} + + +/* dummy set rate lets see what happens */ +static int hdspm_set_rate(hdspm_t * hdspm, int rate, int called_internally) +{ + int reject_if_open = 0; + int current_rate; + int rate_bits; + int not_set = 0; + + /* ASSUMPTION: hdspm->lock is either set, or there is no need for + it (e.g. during module initialization). + */ + + if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { + + /* SLAVE --- */ + if (called_internally) { + + /* request from ctl or card initialization + just make a warning an remember setting + for future master mode switching */ + + snd_printk + (KERN_WARNING "HDSPM: Warning: device is not running as a clock master.\n"); + not_set = 1; + } else { + + /* hw_param request while in AutoSync mode */ + int external_freq = + hdspm_external_sample_rate(hdspm); + + if ((hdspm_autosync_ref(hdspm) == + HDSPM_AUTOSYNC_FROM_NONE)) { + + snd_printk(KERN_WARNING "HDSPM: Detected no Externel Sync \n"); + not_set = 1; + + } else if (rate != external_freq) { + + snd_printk + (KERN_WARNING "HDSPM: Warning: No AutoSync source for requested rate\n"); + not_set = 1; + } + } + } + + current_rate = hdspm->system_sample_rate; + + /* Changing between Singe, Double and Quad speed is not + allowed if any substreams are open. This is because such a change + causes a shift in the location of the DMA buffers and a reduction + in the number of available buffers. + + Note that a similar but essentially insoluble problem exists for + externally-driven rate changes. All we can do is to flag rate + changes in the read/write routines. + */ + + switch (rate) { + case 32000: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency32KHz; + break; + case 44100: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency44_1KHz; + break; + case 48000: + if (current_rate > 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency48KHz; + break; + case 64000: + if (current_rate <= 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency64KHz; + break; + case 88200: + if (current_rate <= 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency88_2KHz; + break; + case 96000: + if (current_rate <= 48000) { + reject_if_open = 1; + } + rate_bits = HDSPM_Frequency96KHz; + break; + default: + return -EINVAL; + } + + if (reject_if_open + && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) { + snd_printk + (KERN_ERR "HDSPM: cannot change between single- and double-speed mode (capture PID = %d, playback PID = %d)\n", + hdspm->capture_pid, hdspm->playback_pid); + return -EBUSY; + } + + hdspm->control_register &= ~HDSPM_FrequencyMask; + hdspm->control_register |= rate_bits; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + if (rate > 64000) + hdspm->channel_map = channel_map_madi_qs; + else if (rate > 48000) + hdspm->channel_map = channel_map_madi_ds; + else + hdspm->channel_map = channel_map_madi_ss; + + hdspm->system_sample_rate = rate; + + if (not_set != 0) + return -1; + + return 0; +} + +/* mainly for init to 0 on load */ +static void all_in_all_mixer(hdspm_t * hdspm, int sgain) +{ + int i, j; + unsigned int gain = + (sgain > UNITY_GAIN) ? UNITY_GAIN : (sgain < 0) ? 0 : sgain; + + for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) + for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) { + hdspm_write_in_gain(hdspm, i, j, gain); + hdspm_write_pb_gain(hdspm, i, j, gain); + } +} + +/*---------------------------------------------------------------------------- + MIDI + ----------------------------------------------------------------------------*/ + +static inline unsigned char snd_hdspm_midi_read_byte (hdspm_t *hdspm, int id) +{ + /* the hardware already does the relevant bit-mask with 0xff */ + if (id) + return hdspm_read(hdspm, HDSPM_midiDataIn1); + else + return hdspm_read(hdspm, HDSPM_midiDataIn0); +} + +static inline void snd_hdspm_midi_write_byte (hdspm_t *hdspm, int id, int val) +{ + /* the hardware already does the relevant bit-mask with 0xff */ + if (id) + return hdspm_write(hdspm, HDSPM_midiDataOut1, val); + else + return hdspm_write(hdspm, HDSPM_midiDataOut0, val); +} + +static inline int snd_hdspm_midi_input_available (hdspm_t *hdspm, int id) +{ + if (id) + return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff); + else + return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff); +} + +static inline int snd_hdspm_midi_output_possible (hdspm_t *hdspm, int id) +{ + int fifo_bytes_used; + + if (id) + fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xff; + else + fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xff; + + if (fifo_bytes_used < 128) + return 128 - fifo_bytes_used; + else + return 0; +} + +static inline void snd_hdspm_flush_midi_input (hdspm_t *hdspm, int id) +{ + while (snd_hdspm_midi_input_available (hdspm, id)) + snd_hdspm_midi_read_byte (hdspm, id); +} + +static int snd_hdspm_midi_output_write (hdspm_midi_t *hmidi) +{ + unsigned long flags; + int n_pending; + int to_write; + int i; + unsigned char buf[128]; + + /* Output is not interrupt driven */ + + spin_lock_irqsave (&hmidi->lock, flags); + if (hmidi->output) { + if (!snd_rawmidi_transmit_empty (hmidi->output)) { + if ((n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm, hmidi->id)) > 0) { + if (n_pending > (int)sizeof (buf)) + n_pending = sizeof (buf); + + if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) { + for (i = 0; i < to_write; ++i) + snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]); + } + } + } + } + spin_unlock_irqrestore (&hmidi->lock, flags); + return 0; +} + +static int snd_hdspm_midi_input_read (hdspm_midi_t *hmidi) +{ + unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */ + unsigned long flags; + int n_pending; + int i; + + spin_lock_irqsave (&hmidi->lock, flags); + if ((n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id)) > 0) { + if (hmidi->input) { + if (n_pending > (int)sizeof (buf)) { + n_pending = sizeof (buf); + } + for (i = 0; i < n_pending; ++i) { + buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id); + } + if (n_pending) { + snd_rawmidi_receive (hmidi->input, buf, n_pending); + } + } else { + /* flush the MIDI input FIFO */ + while (n_pending--) { + snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id); + } + } + } + hmidi->pending = 0; + if (hmidi->id) { + hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable; + } else { + hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable; + } + hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register); + spin_unlock_irqrestore (&hmidi->lock, flags); + return snd_hdspm_midi_output_write (hmidi); +} + +static void snd_hdspm_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + hdspm_t *hdspm; + hdspm_midi_t *hmidi; + unsigned long flags; + u32 ie; + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + hdspm = hmidi->hdspm; + ie = hmidi->id ? HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; + spin_lock_irqsave (&hdspm->lock, flags); + if (up) { + if (!(hdspm->control_register & ie)) { + snd_hdspm_flush_midi_input (hdspm, hmidi->id); + hdspm->control_register |= ie; + } + } else { + hdspm->control_register &= ~ie; + } + + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + spin_unlock_irqrestore (&hdspm->lock, flags); +} + +static void snd_hdspm_midi_output_timer(unsigned long data) +{ + hdspm_midi_t *hmidi = (hdspm_midi_t *) data; + unsigned long flags; + + snd_hdspm_midi_output_write(hmidi); + spin_lock_irqsave (&hmidi->lock, flags); + + /* this does not bump hmidi->istimer, because the + kernel automatically removed the timer when it + expired, and we are now adding it back, thus + leaving istimer wherever it was set before. + */ + + if (hmidi->istimer) { + hmidi->timer.expires = 1 + jiffies; + add_timer(&hmidi->timer); + } + + spin_unlock_irqrestore (&hmidi->lock, flags); +} + +static void snd_hdspm_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + hdspm_midi_t *hmidi; + unsigned long flags; + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + spin_lock_irqsave (&hmidi->lock, flags); + if (up) { + if (!hmidi->istimer) { + init_timer(&hmidi->timer); + hmidi->timer.function = snd_hdspm_midi_output_timer; + hmidi->timer.data = (unsigned long) hmidi; + hmidi->timer.expires = 1 + jiffies; + add_timer(&hmidi->timer); + hmidi->istimer++; + } + } else { + if (hmidi->istimer && --hmidi->istimer <= 0) { + del_timer (&hmidi->timer); + } + } + spin_unlock_irqrestore (&hmidi->lock, flags); + if (up) + snd_hdspm_midi_output_write(hmidi); +} + +static int snd_hdspm_midi_input_open(snd_rawmidi_substream_t * substream) +{ + hdspm_midi_t *hmidi; + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + spin_lock_irq (&hmidi->lock); + snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id); + hmidi->input = substream; + spin_unlock_irq (&hmidi->lock); + + return 0; +} + +static int snd_hdspm_midi_output_open(snd_rawmidi_substream_t * substream) +{ + hdspm_midi_t *hmidi; + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + spin_lock_irq (&hmidi->lock); + hmidi->output = substream; + spin_unlock_irq (&hmidi->lock); + + return 0; +} + +static int snd_hdspm_midi_input_close(snd_rawmidi_substream_t * substream) +{ + hdspm_midi_t *hmidi; + + snd_hdspm_midi_input_trigger (substream, 0); + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + spin_lock_irq (&hmidi->lock); + hmidi->input = NULL; + spin_unlock_irq (&hmidi->lock); + + return 0; +} + +static int snd_hdspm_midi_output_close(snd_rawmidi_substream_t * substream) +{ + hdspm_midi_t *hmidi; + + snd_hdspm_midi_output_trigger (substream, 0); + + hmidi = (hdspm_midi_t *) substream->rmidi->private_data; + spin_lock_irq (&hmidi->lock); + hmidi->output = NULL; + spin_unlock_irq (&hmidi->lock); + + return 0; +} + +snd_rawmidi_ops_t snd_hdspm_midi_output = +{ + .open = snd_hdspm_midi_output_open, + .close = snd_hdspm_midi_output_close, + .trigger = snd_hdspm_midi_output_trigger, +}; + +snd_rawmidi_ops_t snd_hdspm_midi_input = +{ + .open = snd_hdspm_midi_input_open, + .close = snd_hdspm_midi_input_close, + .trigger = snd_hdspm_midi_input_trigger, +}; + +static int __devinit snd_hdspm_create_midi (snd_card_t *card, hdspm_t *hdspm, int id) +{ + int err; + char buf[32]; + + hdspm->midi[id].id = id; + hdspm->midi[id].rmidi = NULL; + hdspm->midi[id].input = NULL; + hdspm->midi[id].output = NULL; + hdspm->midi[id].hdspm = hdspm; + hdspm->midi[id].istimer = 0; + hdspm->midi[id].pending = 0; + spin_lock_init (&hdspm->midi[id].lock); + + sprintf (buf, "%s MIDI %d", card->shortname, id+1); + if ((err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi)) < 0) + return err; + + sprintf (hdspm->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + + snd_rawmidi_set_ops (hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdspm_midi_output); + snd_rawmidi_set_ops (hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdspm_midi_input); + + hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} + + +static void hdspm_midi_tasklet(unsigned long arg) +{ + hdspm_t *hdspm = (hdspm_t *)arg; + + if (hdspm->midi[0].pending) + snd_hdspm_midi_input_read (&hdspm->midi[0]); + if (hdspm->midi[1].pending) + snd_hdspm_midi_input_read (&hdspm->midi[1]); +} + + +/*----------------------------------------------------------------------------- + Status Interface + ----------------------------------------------------------------------------*/ + +/* get the system sample rate which is set */ + +#define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_system_sample_rate, \ + .get = snd_hdspm_get_system_sample_rate \ +} + +static int snd_hdspm_info_system_sample_rate(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + return 0; +} + +static int snd_hdspm_get_system_sample_rate(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate; + return 0; +} + +#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_autosync_sample_rate, \ + .get = snd_hdspm_get_autosync_sample_rate \ +} + +static int snd_hdspm_info_autosync_sample_rate(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "32000", "44100", "48000", + "64000", "88200", "96000", + "128000", "176400", "192000", + "None" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 10; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdspm_get_autosync_sample_rate(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + switch (hdspm_external_sample_rate(hdspm)) { + case 32000: + ucontrol->value.enumerated.item[0] = 0; + break; + case 44100: + ucontrol->value.enumerated.item[0] = 1; + break; + case 48000: + ucontrol->value.enumerated.item[0] = 2; + break; + case 64000: + ucontrol->value.enumerated.item[0] = 3; + break; + case 88200: + ucontrol->value.enumerated.item[0] = 4; + break; + case 96000: + ucontrol->value.enumerated.item[0] = 5; + break; + case 128000: + ucontrol->value.enumerated.item[0] = 6; + break; + case 176400: + ucontrol->value.enumerated.item[0] = 7; + break; + case 192000: + ucontrol->value.enumerated.item[0] = 8; + break; + + default: + ucontrol->value.enumerated.item[0] = 9; + } + return 0; +} + +#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_system_clock_mode, \ + .get = snd_hdspm_get_system_clock_mode, \ +} + + + +static int hdspm_system_clock_mode(hdspm_t * hdspm) +{ + /* Always reflect the hardware info, rme is never wrong !!!! */ + + if (hdspm->control_register & HDSPM_ClockModeMaster) + return 0; + return 1; +} + +static int snd_hdspm_info_system_clock_mode(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "Master", "Slave" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdspm_get_system_clock_mode(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = + hdspm_system_clock_mode(hdspm); + return 0; +} + +#define HDSPM_CLOCK_SOURCE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_clock_source, \ + .get = snd_hdspm_get_clock_source, \ + .put = snd_hdspm_put_clock_source \ +} + +static int hdspm_clock_source(hdspm_t * hdspm) +{ + if (hdspm->control_register & HDSPM_ClockModeMaster) { + switch (hdspm->system_sample_rate) { + case 32000: + return 1; + case 44100: + return 2; + case 48000: + return 3; + case 64000: + return 4; + case 88200: + return 5; + case 96000: + return 6; + case 128000: + return 7; + case 176400: + return 8; + case 192000: + return 9; + default: + return 3; + } + } else { + return 0; + } +} + +static int hdspm_set_clock_source(hdspm_t * hdspm, int mode) +{ + int rate; + switch (mode) { + + case HDSPM_CLOCK_SOURCE_AUTOSYNC: + if (hdspm_external_sample_rate(hdspm) != 0) { + hdspm->control_register &= ~HDSPM_ClockModeMaster; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + return 0; + } + return -1; + case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: + rate = 32000; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: + rate = 44100; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: + rate = 48000; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: + rate = 64000; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: + rate = 88200; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: + rate = 96000; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: + rate = 128000; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: + rate = 176400; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: + rate = 192000; + break; + + default: + rate = 44100; + } + hdspm->control_register |= HDSPM_ClockModeMaster; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + hdspm_set_rate(hdspm, rate, 1); + return 0; +} + +static int snd_hdspm_info_clock_source(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "AutoSync", + "Internal 32.0 kHz", "Internal 44.1 kHz", + "Internal 48.0 kHz", + "Internal 64.0 kHz", "Internal 88.2 kHz", + "Internal 96.0 kHz", + "Internal 128.0 kHz", "Internal 176.4 kHz", + "Internal 192.0 kHz" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 10; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_clock_source(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm); + return 0; +} + +static int snd_hdspm_put_clock_source(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0]; + if (val < 0) + val = 0; + if (val > 6) + val = 6; + spin_lock_irq(&hdspm->lock); + if (val != hdspm_clock_source(hdspm)) + change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0; + else + change = 0; + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_PREF_SYNC_REF(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_pref_sync_ref, \ + .get = snd_hdspm_get_pref_sync_ref, \ + .put = snd_hdspm_put_pref_sync_ref \ +} + +static int hdspm_pref_sync_ref(hdspm_t * hdspm) +{ + /* Notice that this looks at the requested sync source, + not the one actually in use. + */ + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case HDSPM_SyncRef_Word: + return HDSPM_SYNC_FROM_WORD; + case HDSPM_SyncRef_MADI: + return HDSPM_SYNC_FROM_MADI; + } + + return HDSPM_SYNC_FROM_WORD; +} + +static int hdspm_set_pref_sync_ref(hdspm_t * hdspm, int pref) +{ + hdspm->control_register &= ~HDSPM_SyncRefMask; + + switch (pref) { + case HDSPM_SYNC_FROM_MADI: + hdspm->control_register |= HDSPM_SyncRef_MADI; + break; + case HDSPM_SYNC_FROM_WORD: + hdspm->control_register |= HDSPM_SyncRef_Word; + break; + default: + return -1; + } + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + return 0; +} + +static int snd_hdspm_info_pref_sync_ref(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "Word", "MADI" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdspm_get_pref_sync_ref(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm); + return 0; +} + +static int snd_hdspm_put_pref_sync_ref(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change, max; + unsigned int val; + + max = 2; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + val = ucontrol->value.enumerated.item[0] % max; + + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_pref_sync_ref(hdspm); + hdspm_set_pref_sync_ref(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_AUTOSYNC_REF(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_autosync_ref, \ + .get = snd_hdspm_get_autosync_ref, \ +} + +static int hdspm_autosync_ref(hdspm_t * hdspm) +{ + /* This looks at the autosync selected sync reference */ + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + switch (status2 & HDSPM_SelSyncRefMask) { + + case HDSPM_SelSyncRef_WORD: + return HDSPM_AUTOSYNC_FROM_WORD; + + case HDSPM_SelSyncRef_MADI: + return HDSPM_AUTOSYNC_FROM_MADI; + + case HDSPM_SelSyncRef_NVALID: + return HDSPM_AUTOSYNC_FROM_NONE; + + default: + return 0; + } + + return 0; +} + +static int snd_hdspm_info_autosync_ref(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "WordClock", "MADI", "None" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_hdspm_get_autosync_ref(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm); + return 0; +} + +#define HDSPM_LINE_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_line_out, \ + .get = snd_hdspm_get_line_out, \ + .put = snd_hdspm_put_line_out \ +} + +static int hdspm_line_out(hdspm_t * hdspm) +{ + return (hdspm->control_register & HDSPM_LineOut) ? 1 : 0; +} + + +static int hdspm_set_line_output(hdspm_t * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_LineOut; + else + hdspm->control_register &= ~HDSPM_LineOut; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_line_out(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_line_out(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = hdspm_line_out(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_line_out(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_line_out(hdspm); + hdspm_set_line_output(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_TX_64(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_tx_64, \ + .get = snd_hdspm_get_tx_64, \ + .put = snd_hdspm_put_tx_64 \ +} + +static int hdspm_tx_64(hdspm_t * hdspm) +{ + return (hdspm->control_register & HDSPM_TX_64ch) ? 1 : 0; +} + +static int hdspm_set_tx_64(hdspm_t * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_TX_64ch; + else + hdspm->control_register &= ~HDSPM_TX_64ch; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_tx_64(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_tx_64(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = hdspm_tx_64(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_tx_64(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_tx_64(hdspm); + hdspm_set_tx_64(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_C_TMS(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_c_tms, \ + .get = snd_hdspm_get_c_tms, \ + .put = snd_hdspm_put_c_tms \ +} + +static int hdspm_c_tms(hdspm_t * hdspm) +{ + return (hdspm->control_register & HDSPM_clr_tms) ? 1 : 0; +} + +static int hdspm_set_c_tms(hdspm_t * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_clr_tms; + else + hdspm->control_register &= ~HDSPM_clr_tms; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_c_tms(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_c_tms(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = hdspm_c_tms(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_c_tms(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_c_tms(hdspm); + hdspm_set_c_tms(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_SAFE_MODE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_safe_mode, \ + .get = snd_hdspm_get_safe_mode, \ + .put = snd_hdspm_put_safe_mode \ +} + +static int hdspm_safe_mode(hdspm_t * hdspm) +{ + return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0; +} + +static int hdspm_set_safe_mode(hdspm_t * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_AutoInp; + else + hdspm->control_register &= ~HDSPM_AutoInp; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_safe_mode(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_safe_mode(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_safe_mode(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_safe_mode(hdspm); + hdspm_set_safe_mode(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_INPUT_SELECT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_input_select, \ + .get = snd_hdspm_get_input_select, \ + .put = snd_hdspm_put_input_select \ +} + +static int hdspm_input_select(hdspm_t * hdspm) +{ + return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0; +} + +static int hdspm_set_input_select(hdspm_t * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_InputSelect0; + else + hdspm->control_register &= ~HDSPM_InputSelect0; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_input_select(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "optical", "coaxial" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_input_select(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_input_select(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_input_select(hdspm); + hdspm_set_input_select(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +/* Simple Mixer + deprecated since to much faders ??? + MIXER interface says output (source, destination, value) + where source > MAX_channels are playback channels + on MADICARD + - playback mixer matrix: [channelout+64] [output] [value] + - input(thru) mixer matrix: [channelin] [output] [value] + (better do 2 kontrols for seperation ?) +*/ + +#define HDSPM_MIXER(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_mixer, \ + .get = snd_hdspm_get_mixer, \ + .put = snd_hdspm_put_mixer \ +} + +static int snd_hdspm_info_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65535; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_hdspm_get_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int source; + int destination; + + source = ucontrol->value.integer.value[0]; + if (source < 0) + source = 0; + else if (source >= 2 * HDSPM_MAX_CHANNELS) + source = 2 * HDSPM_MAX_CHANNELS - 1; + + destination = ucontrol->value.integer.value[1]; + if (destination < 0) + destination = 0; + else if (destination >= HDSPM_MAX_CHANNELS) + destination = HDSPM_MAX_CHANNELS - 1; + + spin_lock_irq(&hdspm->lock); + if (source >= HDSPM_MAX_CHANNELS) + ucontrol->value.integer.value[2] = + hdspm_read_pb_gain(hdspm, destination, + source - HDSPM_MAX_CHANNELS); + else + ucontrol->value.integer.value[2] = + hdspm_read_in_gain(hdspm, destination, source); + + spin_unlock_irq(&hdspm->lock); + + return 0; +} + +static int snd_hdspm_put_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + int source; + int destination; + int gain; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + source = ucontrol->value.integer.value[0]; + destination = ucontrol->value.integer.value[1]; + + if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS) + return -1; + if (destination < 0 || destination >= HDSPM_MAX_CHANNELS) + return -1; + + gain = ucontrol->value.integer.value[2]; + + spin_lock_irq(&hdspm->lock); + + if (source >= HDSPM_MAX_CHANNELS) + change = gain != hdspm_read_pb_gain(hdspm, destination, + source - + HDSPM_MAX_CHANNELS); + else + change = + gain != hdspm_read_in_gain(hdspm, destination, source); + + if (change) { + if (source >= HDSPM_MAX_CHANNELS) + hdspm_write_pb_gain(hdspm, destination, + source - HDSPM_MAX_CHANNELS, + gain); + else + hdspm_write_in_gain(hdspm, destination, source, + gain); + } + spin_unlock_irq(&hdspm->lock); + + return change; +} + +/* The simple mixer control(s) provide gain control for the + basic 1:1 mappings of playback streams to output + streams. +*/ + +#define HDSPM_PLAYBACK_MIXER \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_playback_mixer, \ + .get = snd_hdspm_get_playback_mixer, \ + .put = snd_hdspm_put_playback_mixer \ +} + +static int snd_hdspm_info_playback_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65536; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_hdspm_get_playback_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int channel; + int mapped_channel; + + channel = ucontrol->id.index - 1; + + snd_assert(channel >= 0 + || channel < HDSPM_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdspm->channel_map[channel]) < 0) + return -EINVAL; + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = + hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel); + spin_unlock_irq(&hdspm->lock); + + /* snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, value %d\n", + ucontrol->id.index, channel, mapped_channel, ucontrol->value.integer.value[0]); + */ + + return 0; +} + +static int snd_hdspm_put_playback_mixer(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + int change; + int channel; + int mapped_channel; + int gain; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + channel = ucontrol->id.index - 1; + + snd_assert(channel >= 0 + || channel < HDSPM_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdspm->channel_map[channel]) < 0) + return -EINVAL; + + gain = ucontrol->value.integer.value[0]; + + spin_lock_irq(&hdspm->lock); + change = + gain != hdspm_read_pb_gain(hdspm, mapped_channel, + mapped_channel); + if (change) + hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel, + gain); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_WC_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_wc_sync_check \ +} + +static int snd_hdspm_info_sync_check(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { "No Lock", "Lock", "Sync" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int hdspm_wc_sync_check(hdspm_t * hdspm) +{ + int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + if (status2 & HDSPM_wcLock) { + if (status2 & HDSPM_wcSync) + return 2; + else + return 1; + } + return 0; +} + +static int snd_hdspm_get_wc_sync_check(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm); + return 0; +} + + +#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_madisync_sync_check \ +} + +static int hdspm_madisync_sync_check(hdspm_t * hdspm) +{ + int status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_madiLock) { + if (status & HDSPM_madiSync) + return 2; + else + return 1; + } + return 0; +} + +static int snd_hdspm_get_madisync_sync_check(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * + ucontrol) +{ + hdspm_t *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = + hdspm_madisync_sync_check(hdspm); + return 0; +} + + + + +static snd_kcontrol_new_t snd_hdspm_controls[] = { + + HDSPM_MIXER("Mixer", 0), +/* 'Sample Clock Source' complies with the alsa control naming scheme */ + HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), + + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), + HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), +/* 'External Rate' complies with the alsa control naming scheme */ + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), + HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0), + HDSPM_LINE_OUT("Line Out", 0), + HDSPM_TX_64("TX 64 channels mode", 0), + HDSPM_C_TMS("Clear Track Marker", 0), + HDSPM_SAFE_MODE("Safe Mode", 0), + HDSPM_INPUT_SELECT("Input Select", 0), +}; + +static snd_kcontrol_new_t snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER; + + +static int hdspm_update_simple_mixer_controls(hdspm_t * hdspm) +{ + int i; + + for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) { + if (hdspm->system_sample_rate > 48000) { + hdspm->playback_mixer_ctls[i]->vd[0].access = + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } else { + hdspm->playback_mixer_ctls[i]->vd[0].access = + SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } + snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &hdspm->playback_mixer_ctls[i]->id); + } + + return 0; +} + + +static int snd_hdspm_create_controls(snd_card_t * card, hdspm_t * hdspm) +{ + unsigned int idx, limit; + int err; + snd_kcontrol_t *kctl; + + /* add control list first */ + + for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls); idx++) { + if ((err = + snd_ctl_add(card, kctl = + snd_ctl_new1(&snd_hdspm_controls[idx], + hdspm))) < 0) { + return err; + } + } + + /* Channel playback mixer as default control + Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats to big for any alsamixer + they are accesible via special IOCTL on hwdep + and the mixer 2dimensional mixer control */ + + snd_hdspm_playback_mixer.name = "Chn"; + limit = HDSPM_MAX_CHANNELS; + + /* The index values are one greater than the channel ID so that alsamixer + will display them correctly. We want to use the index for fast lookup + of the relevant channel, but if we use it at all, most ALSA software + does the wrong thing with it ... + */ + + for (idx = 0; idx < limit; ++idx) { + snd_hdspm_playback_mixer.index = idx + 1; + if ((err = snd_ctl_add(card, + kctl = + snd_ctl_new1 + (&snd_hdspm_playback_mixer, + hdspm)))) { + return err; + } + hdspm->playback_mixer_ctls[idx] = kctl; + } + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_hdspm_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) +{ + hdspm_t *hdspm = (hdspm_t *) entry->private_data; + unsigned int status; + unsigned int status2; + char *pref_sync_ref; + char *autosync_ref; + char *system_clock_mode; + char *clock_source; + char *insel; + char *syncref; + int x, x2; + + status = hdspm_read(hdspm, HDSPM_statusRegister); + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + + snd_iprintf(buffer, "--- System ---\n"); + + snd_iprintf(buffer, + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); + snd_iprintf(buffer, + "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % (2 * + (int)hdspm-> + period_bytes), + ((status & HDSPM_BufferPositionMask) - + 64) % (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); + + snd_iprintf(buffer, + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + + snd_iprintf(buffer, "--- Settings ---\n"); + + x = 1 << (6 + + hdspm_decode_latency(hdspm-> + control_register & + HDSPM_LatencyMask)); + + snd_iprintf(buffer, + "Size (Latency): %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) hdspm->period_bytes); + + snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", + (hdspm-> + control_register & HDSPM_LineOut) ? "on " : "off", + (hdspm->precise_ptr) ? "on" : "off"); + + switch (hdspm->control_register & HDSPM_InputMask) { + case HDSPM_InputOptical: + insel = "Optical"; + break; + case HDSPM_InputCoaxial: + insel = "Coaxial"; + break; + default: + insel = "Unkown"; + } + + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case HDSPM_SyncRef_Word: + syncref = "WordClock"; + break; + case HDSPM_SyncRef_MADI: + syncref = "MADI"; + break; + default: + syncref = "Unkown"; + } + snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel, + syncref); + + snd_iprintf(buffer, + "ClearTrackMarker = %s, Transmit in %s Channel Mode, Auto Input %s\n", + (hdspm-> + control_register & HDSPM_clr_tms) ? "on" : "off", + (hdspm-> + control_register & HDSPM_TX_64ch) ? "64" : "56", + (hdspm-> + control_register & HDSPM_AutoInp) ? "on" : "off"); + + switch (hdspm_clock_source(hdspm)) { + case HDSPM_CLOCK_SOURCE_AUTOSYNC: + clock_source = "AutoSync"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: + clock_source = "Internal 32 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: + clock_source = "Internal 44.1 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: + clock_source = "Internal 48 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: + clock_source = "Internal 64 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: + clock_source = "Internal 88.2 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: + clock_source = "Internal 96 kHz"; + break; + default: + clock_source = "Error"; + } + snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); + if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { + system_clock_mode = "Slave"; + } else { + system_clock_mode = "Master"; + } + snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); + + switch (hdspm_pref_sync_ref(hdspm)) { + case HDSPM_SYNC_FROM_WORD: + pref_sync_ref = "Word Clock"; + break; + case HDSPM_SYNC_FROM_MADI: + pref_sync_ref = "MADI Sync"; + break; + default: + pref_sync_ref = "XXXX Clock"; + break; + } + snd_iprintf(buffer, "Preferred Sync Reference: %s\n", + pref_sync_ref); + + snd_iprintf(buffer, "System Clock Frequency: %d\n", + hdspm->system_sample_rate); + + + snd_iprintf(buffer, "--- Status:\n"); + + x = status & HDSPM_madiSync; + x2 = status2 & HDSPM_wcSync; + + snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n", + (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : + "NoLock", + (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : + "NoLock"); + + switch (hdspm_autosync_ref(hdspm)) { + case HDSPM_AUTOSYNC_FROM_WORD: + autosync_ref = "Word Clock"; + break; + case HDSPM_AUTOSYNC_FROM_MADI: + autosync_ref = "MADI Sync"; + break; + case HDSPM_AUTOSYNC_FROM_NONE: + autosync_ref = "Input not valid"; + break; + default: + autosync_ref = "---"; + break; + } + snd_iprintf(buffer, + "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", + autosync_ref, hdspm_external_sample_rate(hdspm), + (status & HDSPM_madiFreqMask) >> 22, + (status2 & HDSPM_wcFreqMask) >> 5); + + snd_iprintf(buffer, "Input: %s, Mode=%s\n", + (status & HDSPM_AB_int) ? "Coax" : "Optical", + (status & HDSPM_RX_64ch) ? "64 channels" : + "56 channels"); + + snd_iprintf(buffer, "\n"); +} + +static void __devinit snd_hdspm_proc_init(hdspm_t * hdspm) +{ + snd_info_entry_t *entry; + + if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) + snd_info_set_text_ops(entry, hdspm, 1024, + snd_hdspm_proc_read); +} + +/*------------------------------------------------------------ + hdspm intitialize + ------------------------------------------------------------*/ + +static int snd_hdspm_set_defaults(hdspm_t * hdspm) +{ + unsigned int i; + + /* ASSUMPTION: hdspm->lock is either held, or there is no need to + hold it (e.g. during module initalization). + */ + + /* set defaults: */ + + hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ + HDSPM_InputCoaxial | /* Input Coax not Optical */ + HDSPM_SyncRef_MADI | /* Madi is syncclock */ + HDSPM_LineOut | /* Analog output in */ + HDSPM_TX_64ch | /* transmit in 64ch mode */ + HDSPM_AutoInp; /* AutoInput chossing (takeover) */ + + /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ + /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ + /* ! HDSPM_clr_tms = do not clear bits in track marks */ + + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + +#ifdef SNDRV_BIG_ENDIAN + hdspm->control2_register = HDSPM_BIGENDIAN_MODE; +#else + hdspm->control2_register = 0; +#endif + + hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register); + hdspm_compute_period_size(hdspm); + + /* silence everything */ + + all_in_all_mixer(hdspm, 0 * UNITY_GAIN); + + if (line_outs_monitor[hdspm->dev]) { + + snd_printk(KERN_INFO "HDSPM: sending all playback streams to line outs.\n"); + + for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) { + if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN)) + return -EIO; + } + } + + /* set a default rate so that the channel map is set up. */ + hdspm->channel_map = channel_map_madi_ss; + hdspm_set_rate(hdspm, 44100, 1); + + return 0; +} + + +/*------------------------------------------------------------ + interupt + ------------------------------------------------------------*/ + +static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + hdspm_t *hdspm = (hdspm_t *) dev_id; + unsigned int status; + int audio; + int midi0; + int midi1; + unsigned int midi0status; + unsigned int midi1status; + int schedule = 0; + + status = hdspm_read(hdspm, HDSPM_statusRegister); + + audio = status & HDSPM_audioIRQPending; + midi0 = status & HDSPM_midi0IRQPending; + midi1 = status & HDSPM_midi1IRQPending; + + if (!audio && !midi0 && !midi1) + return IRQ_NONE; + + hdspm_write(hdspm, HDSPM_interruptConfirmation, 0); + hdspm->irq_count++; + + midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff; + midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff; + + if (audio) { + + if (hdspm->capture_substream) + snd_pcm_period_elapsed(hdspm->pcm-> + streams + [SNDRV_PCM_STREAM_CAPTURE]. + substream); + + if (hdspm->playback_substream) + snd_pcm_period_elapsed(hdspm->pcm-> + streams + [SNDRV_PCM_STREAM_PLAYBACK]. + substream); + } + + if (midi0 && midi0status) { + /* we disable interrupts for this input until processing is done */ + hdspm->control_register &= ~HDSPM_Midi0InterruptEnable; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + hdspm->midi[0].pending = 1; + schedule = 1; + } + if (midi1 && midi1status) { + /* we disable interrupts for this input until processing is done */ + hdspm->control_register &= ~HDSPM_Midi1InterruptEnable; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + hdspm->midi[1].pending = 1; + schedule = 1; + } + if (schedule) + tasklet_hi_schedule(&hdspm->midi_tasklet); + return IRQ_HANDLED; +} + +/*------------------------------------------------------------ + pcm interface + ------------------------------------------------------------*/ + + +static snd_pcm_uframes_t snd_hdspm_hw_pointer(snd_pcm_substream_t * + substream) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + return hdspm_hw_pointer(hdspm); +} + +static char *hdspm_channel_buffer_location(hdspm_t * hdspm, + int stream, int channel) +{ + int mapped_channel; + + snd_assert(channel >= 0 + || channel < HDSPM_MAX_CHANNELS, return NULL); + + if ((mapped_channel = hdspm->channel_map[channel]) < 0) + return NULL; + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return hdspm->capture_buffer + + mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; + } else { + return hdspm->playback_buffer + + mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; + } +} + + +/* dont know why need it ??? */ +static int snd_hdspm_playback_copy(snd_pcm_substream_t * substream, + int channel, snd_pcm_uframes_t pos, + void __user *src, snd_pcm_uframes_t count) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, + return -EINVAL); + + channel_buf = hdspm_channel_buffer_location(hdspm, + substream->pstr-> + stream, channel); + + snd_assert(channel_buf != NULL, return -EIO); + + return copy_from_user(channel_buf + pos * 4, src, count * 4); +} + +static int snd_hdspm_capture_copy(snd_pcm_substream_t * substream, + int channel, snd_pcm_uframes_t pos, + void __user *dst, snd_pcm_uframes_t count) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, + return -EINVAL); + + channel_buf = hdspm_channel_buffer_location(hdspm, + substream->pstr-> + stream, channel); + snd_assert(channel_buf != NULL, return -EIO); + return copy_to_user(dst, channel_buf + pos * 4, count * 4); +} + +static int snd_hdspm_hw_silence(snd_pcm_substream_t * substream, + int channel, snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = + hdspm_channel_buffer_location(hdspm, substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + memset(channel_buf + pos * 4, 0, count * 4); + return 0; +} + +static int snd_hdspm_reset(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = hdspm->capture_substream; + else + other = hdspm->playback_substream; + + if (hdspm->running) + runtime->status->hw_ptr = hdspm_hw_pointer(hdspm); + else + runtime->status->hw_ptr = 0; + if (other) { + struct list_head *pos; + snd_pcm_substream_t *s; + snd_pcm_runtime_t *oruntime = other->runtime; + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + if (s == other) { + oruntime->status->hw_ptr = + runtime->status->hw_ptr; + break; + } + } + } + return 0; +} + +static int snd_hdspm_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * params) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + int err; + int i; + pid_t this_pid; + pid_t other_pid; + struct snd_sg_buf *sgbuf; + + + spin_lock_irq(&hdspm->lock); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + this_pid = hdspm->playback_pid; + other_pid = hdspm->capture_pid; + } else { + this_pid = hdspm->capture_pid; + other_pid = hdspm->playback_pid; + } + + if ((other_pid > 0) && (this_pid != other_pid)) { + + /* The other stream is open, and not by the same + task as this one. Make sure that the parameters + that matter are the same. + */ + + if (params_rate(params) != hdspm->system_sample_rate) { + spin_unlock_irq(&hdspm->lock); + _snd_pcm_hw_param_setempty(params, + SNDRV_PCM_HW_PARAM_RATE); + return -EBUSY; + } + + if (params_period_size(params) != hdspm->period_bytes / 4) { + spin_unlock_irq(&hdspm->lock); + _snd_pcm_hw_param_setempty(params, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return -EBUSY; + } + + } + /* We're fine. */ + spin_unlock_irq(&hdspm->lock); + + /* how to make sure that the rate matches an externally-set one ? */ + + spin_lock_irq(&hdspm->lock); + if ((err = hdspm_set_rate(hdspm, params_rate(params), 0)) < 0) { + spin_unlock_irq(&hdspm->lock); + _snd_pcm_hw_param_setempty(params, + SNDRV_PCM_HW_PARAM_RATE); + return err; + } + spin_unlock_irq(&hdspm->lock); + + if ((err = + hdspm_set_interrupt_interval(hdspm, + params_period_size(params))) < + 0) { + _snd_pcm_hw_param_setempty(params, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return err; + } + + /* Memory allocation, takashi's method, dont know if we should spinlock */ + /* malloc all buffer even if not enabled to get sure */ + /* malloc only needed bytes */ + err = + snd_pcm_lib_malloc_pages(substream, + HDSPM_CHANNEL_BUFFER_BYTES * + params_channels(params)); + if (err < 0) + return err; + + sgbuf = snd_pcm_substream_sgbuf(substream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + + hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferOut, + params_channels(params)); + + for (i = 0; i < params_channels(params); ++i) + snd_hdspm_enable_out(hdspm, i, 1); + + hdspm->playback_buffer = + (unsigned char *) substream->runtime->dma_area; + } else { + hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn, + params_channels(params)); + + for (i = 0; i < params_channels(params); ++i) + snd_hdspm_enable_in(hdspm, i, 1); + + hdspm->capture_buffer = + (unsigned char *) substream->runtime->dma_area; + } + return 0; +} + +static int snd_hdspm_hw_free(snd_pcm_substream_t * substream) +{ + int i; + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + + /* params_channels(params) should be enough, + but to get sure in case of error */ + for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + snd_hdspm_enable_out(hdspm, i, 0); + + hdspm->playback_buffer = NULL; + } else { + for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + snd_hdspm_enable_in(hdspm, i, 0); + + hdspm->capture_buffer = NULL; + + } + + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int snd_hdspm_channel_info(snd_pcm_substream_t * substream, + snd_pcm_channel_info_t * info) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + int mapped_channel; + + snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL); + + if ((mapped_channel = hdspm->channel_map[info->channel]) < 0) + return -EINVAL; + + info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; + info->first = 0; + info->step = 32; + return 0; +} + +static int snd_hdspm_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + { + return snd_hdspm_reset(substream); + } + + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + { + snd_pcm_channel_info_t *info = arg; + return snd_hdspm_channel_info(substream, info); + } + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_hdspm_trigger(snd_pcm_substream_t * substream, int cmd) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + int running; + + spin_lock(&hdspm->lock); + running = hdspm->running; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running |= 1 << substream->stream; + break; + case SNDRV_PCM_TRIGGER_STOP: + running &= ~(1 << substream->stream); + break; + default: + snd_BUG(); + spin_unlock(&hdspm->lock); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = hdspm->capture_substream; + else + other = hdspm->playback_substream; + + if (other) { + struct list_head *pos; + snd_pcm_substream_t *s; + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + if (s == other) { + snd_pcm_trigger_done(s, substream); + if (cmd == SNDRV_PCM_TRIGGER_START) + running |= 1 << s->stream; + else + running &= ~(1 << s->stream); + goto _ok; + } + } + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) + && substream->stream == + SNDRV_PCM_STREAM_CAPTURE) + hdspm_silence_playback(hdspm); + } else { + if (running && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + hdspm_silence_playback(hdspm); + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hdspm_silence_playback(hdspm); + } + _ok: + snd_pcm_trigger_done(substream, substream); + if (!hdspm->running && running) + hdspm_start_audio(hdspm); + else if (hdspm->running && !running) + hdspm_stop_audio(hdspm); + hdspm->running = running; + spin_unlock(&hdspm->lock); + + return 0; +} + +static int snd_hdspm_prepare(snd_pcm_substream_t * substream) +{ + return 0; +} + +static unsigned int period_sizes[] = + { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; + +static snd_pcm_hardware_t snd_hdspm_playback_subinfo = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = HDSPM_MAX_CHANNELS, + .buffer_bytes_max = + HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, + .period_bytes_min = (64 * 4), + .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0 +}; + +static snd_pcm_hardware_t snd_hdspm_capture_subinfo = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = HDSPM_MAX_CHANNELS, + .buffer_bytes_max = + HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, + .period_bytes_min = (64 * 4), + .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + .count = ARRAY_SIZE(period_sizes), + .list = period_sizes, + .mask = 0 +}; + + +static int snd_hdspm_hw_rule_channels_rate(snd_pcm_hw_params_t * params, + snd_pcm_hw_rule_t * rule) +{ + hdspm_t *hdspm = rule->private; + snd_interval_t *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (r->min > 48000) { + snd_interval_t t = { + .min = 1, + .max = hdspm->ds_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 64000) { + snd_interval_t t = { + .min = 1, + .max = hdspm->ss_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + return 0; +} + +static int snd_hdspm_hw_rule_rate_channels(snd_pcm_hw_params_t * params, + snd_pcm_hw_rule_t * rule) +{ + hdspm_t *hdspm = rule->private; + snd_interval_t *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (c->min <= hdspm->ss_channels) { + snd_interval_t t = { + .min = 32000, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max > hdspm->ss_channels) { + snd_interval_t t = { + .min = 64000, + .max = 96000, + .integer = 1, + }; + + return snd_interval_refine(r, &t); + } + return 0; +} + +static int snd_hdspm_playback_open(snd_pcm_substream_t * substream) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_printdd("Open device substream %d\n", substream->stream); + + spin_lock_irq(&hdspm->lock); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_hdspm_playback_subinfo; + + if (hdspm->capture_substream == NULL) + hdspm_stop_audio(hdspm); + + hdspm->playback_pid = current->pid; + hdspm->playback_substream = substream; + + spin_unlock_irq(&hdspm->lock); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_hdspm_hw_rule_rate_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + return 0; +} + +static int snd_hdspm_playback_release(snd_pcm_substream_t * substream) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + + spin_lock_irq(&hdspm->lock); + + hdspm->playback_pid = -1; + hdspm->playback_substream = NULL; + + spin_unlock_irq(&hdspm->lock); + + return 0; +} + + +static int snd_hdspm_capture_open(snd_pcm_substream_t * substream) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&hdspm->lock); + snd_pcm_set_sync(substream); + runtime->hw = snd_hdspm_capture_subinfo; + + if (hdspm->playback_substream == NULL) + hdspm_stop_audio(hdspm); + + hdspm->capture_pid = current->pid; + hdspm->capture_substream = substream; + + spin_unlock_irq(&hdspm->lock); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_hdspm_hw_rule_rate_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + return 0; +} + +static int snd_hdspm_capture_release(snd_pcm_substream_t * substream) +{ + hdspm_t *hdspm = snd_pcm_substream_chip(substream); + + spin_lock_irq(&hdspm->lock); + + hdspm->capture_pid = -1; + hdspm->capture_substream = NULL; + + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_hwdep_dummy_op(snd_hwdep_t * hw, struct file *file) +{ + /* we have nothing to initialize but the call is required */ + return 0; +} + + +static int snd_hdspm_hwdep_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + hdspm_t *hdspm = (hdspm_t *) hw->private_data; + struct sndrv_hdspm_mixer_ioctl mixer; + hdspm_config_info_t info; + hdspm_version_t hdspm_version; + struct sndrv_hdspm_peak_rms_ioctl rms; + + switch (cmd) { + + + case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS: + if (copy_from_user(&rms, (void __user *)arg, sizeof(rms))) + return -EFAULT; + /* maybe there is a chance to memorymap in future so dont touch just copy */ + if(copy_to_user_fromio((void __user *)rms.peak, + hdspm->iobase+HDSPM_MADI_peakrmsbase, + sizeof(hdspm_peak_rms_t)) != 0 ) + return -EFAULT; + + break; + + + case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: + + spin_lock_irq(&hdspm->lock); + info.pref_sync_ref = + (unsigned char) hdspm_pref_sync_ref(hdspm); + info.wordclock_sync_check = + (unsigned char) hdspm_wc_sync_check(hdspm); + + info.system_sample_rate = hdspm->system_sample_rate; + info.autosync_sample_rate = + hdspm_external_sample_rate(hdspm); + info.system_clock_mode = + (unsigned char) hdspm_system_clock_mode(hdspm); + info.clock_source = + (unsigned char) hdspm_clock_source(hdspm); + info.autosync_ref = + (unsigned char) hdspm_autosync_ref(hdspm); + info.line_out = (unsigned char) hdspm_line_out(hdspm); + info.passthru = 0; + spin_unlock_irq(&hdspm->lock); + if (copy_to_user((void __user *) arg, &info, sizeof(info))) + return -EFAULT; + break; + + case SNDRV_HDSPM_IOCTL_GET_VERSION: + hdspm_version.firmware_rev = hdspm->firmware_rev; + if (copy_to_user((void __user *) arg, &hdspm_version, + sizeof(hdspm_version))) + return -EFAULT; + break; + + case SNDRV_HDSPM_IOCTL_GET_MIXER: + if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer))) + return -EFAULT; + if (copy_to_user + ((void __user *)mixer.mixer, hdspm->mixer, sizeof(hdspm_mixer_t))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_ops_t snd_hdspm_playback_ops = { + .open = snd_hdspm_playback_open, + .close = snd_hdspm_playback_release, + .ioctl = snd_hdspm_ioctl, + .hw_params = snd_hdspm_hw_params, + .hw_free = snd_hdspm_hw_free, + .prepare = snd_hdspm_prepare, + .trigger = snd_hdspm_trigger, + .pointer = snd_hdspm_hw_pointer, + .copy = snd_hdspm_playback_copy, + .silence = snd_hdspm_hw_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + +static snd_pcm_ops_t snd_hdspm_capture_ops = { + .open = snd_hdspm_capture_open, + .close = snd_hdspm_capture_release, + .ioctl = snd_hdspm_ioctl, + .hw_params = snd_hdspm_hw_params, + .hw_free = snd_hdspm_hw_free, + .prepare = snd_hdspm_prepare, + .trigger = snd_hdspm_trigger, + .pointer = snd_hdspm_hw_pointer, + .copy = snd_hdspm_capture_copy, + .page = snd_pcm_sgbuf_ops_page, +}; + +static int __devinit snd_hdspm_create_hwdep(snd_card_t * card, + hdspm_t * hdspm) +{ + snd_hwdep_t *hw; + int err; + + if ((err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw)) < 0) + return err; + + hdspm->hwdep = hw; + hw->private_data = hdspm; + strcpy(hw->name, "HDSPM hwdep interface"); + + hw->ops.open = snd_hdspm_hwdep_dummy_op; + hw->ops.ioctl = snd_hdspm_hwdep_ioctl; + hw->ops.release = snd_hdspm_hwdep_dummy_op; + + return 0; +} + + +/*------------------------------------------------------------ + memory interface + ------------------------------------------------------------*/ +static int __devinit snd_hdspm_preallocate_memory(hdspm_t * hdspm) +{ + int err; + snd_pcm_t *pcm; + size_t wanted; + + pcm = hdspm->pcm; + + wanted = HDSPM_DMA_AREA_BYTES + 4096; /* dont know why, but it works */ + + if ((err = + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(hdspm->pci), + wanted, + wanted)) < 0) { + snd_printdd("Could not preallocate %d Bytes\n", wanted); + + return err; + } else + snd_printdd(" Preallocated %d Bytes\n", wanted); + + return 0; +} + +static int snd_hdspm_memory_free(hdspm_t * hdspm) +{ + snd_printdd("memory_free_for_all %p\n", hdspm->pcm); + + snd_pcm_lib_preallocate_free_for_all(hdspm->pcm); + return 0; +} + + +static void hdspm_set_sgbuf(hdspm_t * hdspm, struct snd_sg_buf *sgbuf, + unsigned int reg, int channels) +{ + int i; + for (i = 0; i < (channels * 16); i++) + hdspm_write(hdspm, reg + 4 * i, + snd_pcm_sgbuf_get_addr(sgbuf, + (size_t) 4096 * i)); +} + +/* ------------- ALSA Devices ---------------------------- */ +static int __devinit snd_hdspm_create_pcm(snd_card_t * card, + hdspm_t * hdspm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm)) < 0) + return err; + + hdspm->pcm = pcm; + pcm->private_data = hdspm; + strcpy(pcm->name, hdspm->card_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_hdspm_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_hdspm_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + if ((err = snd_hdspm_preallocate_memory(hdspm)) < 0) + return err; + + return 0; +} + +static inline void snd_hdspm_initialize_midi_flush(hdspm_t * hdspm) +{ + snd_hdspm_flush_midi_input(hdspm, 0); + snd_hdspm_flush_midi_input(hdspm, 1); +} + +static int __devinit snd_hdspm_create_alsa_devices(snd_card_t * card, + hdspm_t * hdspm) +{ + int err; + + snd_printdd("Create card...\n"); + if ((err = snd_hdspm_create_pcm(card, hdspm)) < 0) + return err; + + if ((err = snd_hdspm_create_midi(card, hdspm, 0)) < 0) + return err; + + if ((err = snd_hdspm_create_midi(card, hdspm, 1)) < 0) + return err; + + if ((err = snd_hdspm_create_controls(card, hdspm)) < 0) + return err; + + if ((err = snd_hdspm_create_hwdep(card, hdspm)) < 0) + return err; + + snd_printdd("proc init...\n"); + snd_hdspm_proc_init(hdspm); + + hdspm->system_sample_rate = -1; + hdspm->last_external_sample_rate = -1; + hdspm->last_internal_sample_rate = -1; + hdspm->playback_pid = -1; + hdspm->capture_pid = -1; + hdspm->capture_substream = NULL; + hdspm->playback_substream = NULL; + + snd_printdd("Set defaults...\n"); + if ((err = snd_hdspm_set_defaults(hdspm)) < 0) + return err; + + snd_printdd("Update mixer controls...\n"); + hdspm_update_simple_mixer_controls(hdspm); + + snd_printdd("Initializeing complete ???\n"); + + if ((err = snd_card_register(card)) < 0) { + snd_printk(KERN_ERR "HDSPM: error registering card\n"); + return err; + } + + snd_printdd("... yes now\n"); + + return 0; +} + +static int __devinit snd_hdspm_create(snd_card_t * card, hdspm_t * hdspm, + int precise_ptr, int enable_monitor) +{ + struct pci_dev *pci = hdspm->pci; + int err; + int i; + + unsigned long io_extent; + + hdspm->irq = -1; + hdspm->irq_count = 0; + + hdspm->midi[0].rmidi = NULL; + hdspm->midi[1].rmidi = NULL; + hdspm->midi[0].input = NULL; + hdspm->midi[1].input = NULL; + hdspm->midi[0].output = NULL; + hdspm->midi[1].output = NULL; + spin_lock_init(&hdspm->midi[0].lock); + spin_lock_init(&hdspm->midi[1].lock); + hdspm->iobase = NULL; + hdspm->control_register = 0; + hdspm->control2_register = 0; + + hdspm->playback_buffer = NULL; + hdspm->capture_buffer = NULL; + + for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + hdspm->playback_mixer_ctls[i] = NULL; + hdspm->mixer = NULL; + + hdspm->card = card; + + spin_lock_init(&hdspm->lock); + + tasklet_init(&hdspm->midi_tasklet, + hdspm_midi_tasklet, (unsigned long) hdspm); + + pci_read_config_word(hdspm->pci, + PCI_CLASS_REVISION, &hdspm->firmware_rev); + + strcpy(card->driver, "HDSPM"); + strcpy(card->mixername, "Xilinx FPGA"); + hdspm->card_name = "RME HDSPM MADI"; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + pci_set_master(hdspm->pci); + + if ((err = pci_request_regions(pci, "hdspm")) < 0) + return err; + + hdspm->port = pci_resource_start(pci, 0); + io_extent = pci_resource_len(pci, 0); + + snd_printdd("grabbed memory region 0x%lx-0x%lx\n", + hdspm->port, hdspm->port + io_extent - 1); + + + if ((hdspm->iobase = ioremap_nocache(hdspm->port, io_extent)) == NULL) { + snd_printk(KERN_ERR "HDSPM: unable to remap region 0x%lx-0x%lx\n", + hdspm->port, hdspm->port + io_extent - 1); + return -EBUSY; + } + snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n", + (unsigned long)hdspm->iobase, hdspm->port, + hdspm->port + io_extent - 1); + + if (request_irq(pci->irq, snd_hdspm_interrupt, + SA_INTERRUPT | SA_SHIRQ, "hdspm", + (void *) hdspm)) { + snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); + return -EBUSY; + } + + snd_printdd("use IRQ %d\n", pci->irq); + + hdspm->irq = pci->irq; + hdspm->precise_ptr = precise_ptr; + + hdspm->monitor_outs = enable_monitor; + + snd_printdd("kmalloc Mixer memory of %d Bytes\n", + sizeof(hdspm_mixer_t)); + if ((hdspm->mixer = + (hdspm_mixer_t *) kmalloc(sizeof(hdspm_mixer_t), GFP_KERNEL)) + == NULL) { + snd_printk(KERN_ERR "HDSPM: unable to kmalloc Mixer memory of %d Bytes\n", + (int)sizeof(hdspm_mixer_t)); + return err; + } + + hdspm->ss_channels = MADI_SS_CHANNELS; + hdspm->ds_channels = MADI_DS_CHANNELS; + hdspm->qs_channels = MADI_QS_CHANNELS; + + snd_printdd("create alsa devices.\n"); + if ((err = snd_hdspm_create_alsa_devices(card, hdspm)) < 0) + return err; + + snd_hdspm_initialize_midi_flush(hdspm); + + return 0; +} + +static int snd_hdspm_free(hdspm_t * hdspm) +{ + + if (hdspm->port) { + + /* stop th audio, and cancel all interrupts */ + hdspm->control_register &= + ~(HDSPM_Start | HDSPM_AudioInterruptEnable + | HDSPM_Midi0InterruptEnable | + HDSPM_Midi1InterruptEnable); + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } + + if (hdspm->irq >= 0) + free_irq(hdspm->irq, (void *) hdspm); + + + if (hdspm->mixer) + kfree(hdspm->mixer); + + if (hdspm->iobase) + iounmap(hdspm->iobase); + + snd_hdspm_memory_free(hdspm); + + if (hdspm->port) + pci_release_regions(hdspm->pci); + + pci_disable_device(hdspm->pci); + return 0; +} + +static void snd_hdspm_card_free(snd_card_t * card) +{ + hdspm_t *hdspm = (hdspm_t *) card->private_data; + + if (hdspm) + snd_hdspm_free(hdspm); +} + +static int __devinit snd_hdspm_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + hdspm_t *hdspm; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + if (!(card = snd_card_new(index[dev], id[dev], + THIS_MODULE, sizeof(hdspm_t)))) + return -ENOMEM; + + hdspm = (hdspm_t *) card->private_data; + card->private_free = snd_hdspm_card_free; + hdspm->dev = dev; + hdspm->pci = pci; + + if ((err = + snd_hdspm_create(card, hdspm, precise_ptr[dev], + enable_monitor[dev])) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->shortname, "HDSPM MADI"); + sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name, + hdspm->port, hdspm->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + + dev++; + return 0; +} + +static void __devexit snd_hdspm_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "RME Hammerfall DSP MADI", + .id_table = snd_hdspm_ids, + .probe = snd_hdspm_probe, + .remove = __devexit_p(snd_hdspm_remove), +}; + + +static int __init alsa_card_hdspm_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit alsa_card_hdspm_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_hdspm_init) +module_exit(alsa_card_hdspm_exit) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 69cd81e..f303740 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -303,18 +303,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer { dmab->dev.type = SNDRV_DMA_TYPE_DEV; dmab->dev.dev = snd_dma_pci_data(pci); - if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), - size, dmab) < 0) - return -ENOMEM; + if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { + if (dmab->bytes >= size) + return 0; } + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, dmab) < 0) + return -ENOMEM; return 0; } static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci) { - if (dmab->area) + if (dmab->area) { + dmab->dev.dev = NULL; /* make it anonymous */ snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci)); + } } @@ -2664,7 +2668,7 @@ static struct pci_driver driver = { static int __init alsa_card_hammerfall_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_hammerfall_exit(void) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index cfd2c5f..60ecb2b 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1522,7 +1522,7 @@ static struct pci_driver driver = { static int __init alsa_card_sonicvibes_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_sonicvibes_exit(void) diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index ad58e08..940d531 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -143,7 +143,8 @@ static int __devinit snd_trident_probe(struct pci_dev *pci, return err; } } - if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, + if (trident->device != TRIDENT_DEVICE_ID_SI7018 && + (err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, trident->midi_port, 1, trident->irq, 0, &trident->rmidi)) < 0) { snd_card_free(card); @@ -184,7 +185,7 @@ static struct pci_driver driver = { static int __init alsa_card_trident_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_trident_exit(void) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 9b4d74d..42c48f0 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -101,7 +101,7 @@ MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); module_param_array(ac97_quirk, charp, NULL, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); module_param_array(dxs_support, int, NULL, 0444); -MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA)"); +MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)"); /* pci ids */ @@ -302,6 +302,7 @@ DEFINE_VIA_REGSET(CAPTURE_8233, 0x60); #define VIA_DXS_DISABLE 2 #define VIA_DXS_48K 3 #define VIA_DXS_NO_VRA 4 +#define VIA_DXS_SRC 5 /* @@ -380,6 +381,7 @@ struct _snd_via82xx { struct via_rate_lock rates[2]; /* playback and capture */ unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ + unsigned int dxs_src: 1; /* use full SRC capabilities of DXS */ unsigned int spdif_on: 1; /* only spdif rates work to external DACs */ snd_pcm_t *pcms[2]; @@ -489,10 +491,8 @@ static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, snd_dma_free_pages(&dev->table); dev->table.area = NULL; } - if (dev->idx_table) { - kfree(dev->idx_table); - dev->idx_table = NULL; - } + kfree(dev->idx_table); + dev->idx_table = NULL; return 0; } @@ -924,15 +924,17 @@ static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream) via82xx_t *chip = snd_pcm_substream_chip(substream); viadev_t *viadev = (viadev_t *)substream->runtime->private_data; snd_pcm_runtime_t *runtime = substream->runtime; + int ac97_rate = chip->dxs_src ? 48000 : runtime->rate; int rate_changed; u32 rbits; - if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0) + if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0) return rate_changed; if (rate_changed) { snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, chip->no_vra ? 48000 : runtime->rate); - snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, + chip->no_vra ? 48000 : runtime->rate); } if (runtime->rate == 48000) rbits = 0xfffff; @@ -1074,6 +1076,12 @@ static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_subst /* fixed DXS playback rate */ runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; + } else if (chip->dxs_src && viadev->reg_offset < 0x40) { + /* use full SRC capabilities of DXS */ + runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_48000); + runtime->hw.rate_min = 8000; + runtime->hw.rate_max = 48000; } else if (! ratep->rate) { int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC; runtime->hw.rates = chip->ac97->rates[idx]; @@ -1550,51 +1558,51 @@ static void snd_via82xx_mixer_free_ac97(ac97_t *ac97) static struct ac97_quirk ac97_quirks[] = { { - .vendor = 0x1106, - .device = 0x4161, + .subvendor = 0x1106, + .subdevice = 0x4161, .codec_id = 0x56494161, /* VT1612A */ .name = "Soltek SL-75DRV5", .type = AC97_TUNE_NONE }, { /* FIXME: which codec? */ - .vendor = 0x1106, - .device = 0x4161, + .subvendor = 0x1106, + .subdevice = 0x4161, .name = "ASRock K7VT2", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1019, - .device = 0x0a81, + .subvendor = 0x1019, + .subdevice = 0x0a81, .name = "ECS K7VTA3", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1019, - .device = 0x0a85, + .subvendor = 0x1019, + .subdevice = 0x0a85, .name = "ECS L7VMM2", .type = AC97_TUNE_HP_ONLY }, { - .vendor = 0x1849, - .device = 0x3059, + .subvendor = 0x1849, + .subdevice = 0x3059, .name = "ASRock K7VM2", .type = AC97_TUNE_HP_ONLY /* VT1616 */ }, { - .vendor = 0x14cd, - .device = 0x7002, + .subvendor = 0x14cd, + .subdevice = 0x7002, .name = "Unknown", .type = AC97_TUNE_ALC_JACK }, { - .vendor = 0x1071, - .device = 0x8590, + .subvendor = 0x1071, + .subdevice = 0x8590, .name = "Mitac Mobo", .type = AC97_TUNE_ALC_JACK }, { - .vendor = 0x161f, - .device = 0x202b, + .subvendor = 0x161f, + .subdevice = 0x202b, .name = "Arima Notebook", .type = AC97_TUNE_HP_ONLY, }, @@ -2132,8 +2140,8 @@ static struct via823x_info via823x_cards[] __devinitdata = { * auto detection of DXS channel supports. */ struct dxs_whitelist { - unsigned short vendor; - unsigned short device; + unsigned short subvendor; + unsigned short subdevice; unsigned short mask; short action; /* new dxs_support value */ }; @@ -2141,38 +2149,44 @@ struct dxs_whitelist { static int __devinit check_dxs_list(struct pci_dev *pci) { static struct dxs_whitelist whitelist[] = { - { .vendor = 0x1005, .device = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */ - { .vendor = 0x1019, .device = 0x0996, .action = VIA_DXS_48K }, - { .vendor = 0x1019, .device = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */ - { .vendor = 0x1019, .device = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */ - { .vendor = 0x1025, .device = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */ - { .vendor = 0x1043, .device = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/ - { .vendor = 0x1043, .device = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */ - { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ - { .vendor = 0x1071, .device = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */ - { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ - { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ - { .vendor = 0x1106, .device = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */ - { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ - { .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */ - { .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ - { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ - { .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ - { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ - { .vendor = 0x1462, .device = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */ - { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ - { .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ - { .vendor = 0x147b, .device = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */ - { .vendor = 0x147b, .device = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */ - { .vendor = 0x147b, .device = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */ - { .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ - { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ - { .vendor = 0x1584, .device = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */ - { .vendor = 0x161f, .device = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */ - { .vendor = 0x161f, .device = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */ - { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ - { .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ - { .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ + { .subvendor = 0x1005, .subdevice = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */ + { .subvendor = 0x1019, .subdevice = 0x0996, .action = VIA_DXS_48K }, + { .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */ + { .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */ + { .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */ + { .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/ + { .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */ + { .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ + { .subvendor = 0x1043, .subdevice = 0x812a, .action = VIA_DXS_SRC }, /* ASUS A8V Deluxe */ + { .subvendor = 0x1071, .subdevice = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */ + { .subvendor = 0x1071, .subdevice = 0x8399, .action = VIA_DXS_NO_VRA }, /* Umax AB 595T (VIA K8N800A - VT8237) */ + { .subvendor = 0x10cf, .subdevice = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ + { .subvendor = 0x1106, .subdevice = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ + { .subvendor = 0x1106, .subdevice = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */ + { .subvendor = 0x1106, .subdevice = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ + { .subvendor = 0x1106, .subdevice = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */ + { .subvendor = 0x1297, .subdevice = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */ + { .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ + { .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ + { .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */ + { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ + { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ + { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */ + { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ + { .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ + { .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */ + { .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */ + { .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */ + { .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ + { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_NO_VRA }, /* Twinhead mobo */ + { .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ + { .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */ + { .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */ + { .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */ + { .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ + { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ + { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ + { .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */ { } /* terminator */ }; struct dxs_whitelist *w; @@ -2182,14 +2196,14 @@ static int __devinit check_dxs_list(struct pci_dev *pci) pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); - for (w = whitelist; w->vendor; w++) { - if (w->vendor != subsystem_vendor) + for (w = whitelist; w->subvendor; w++) { + if (w->subvendor != subsystem_vendor) continue; if (w->mask) { - if ((w->mask & subsystem_device) == w->device) + if ((w->mask & subsystem_device) == w->subdevice) return w->action; } else { - if (subsystem_device == w->device) + if (subsystem_device == w->subdevice) return w->action; } } @@ -2198,8 +2212,9 @@ static int __devinit check_dxs_list(struct pci_dev *pci) * not detected, try 48k rate only to be sure. */ printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n"); - printk(KERN_INFO " Please try dxs_support=1 or dxs_support=4 option\n"); + printk(KERN_INFO " Please try dxs_support=5 option\n"); printk(KERN_INFO " and report if it works on your machine.\n"); + printk(KERN_INFO " For more details, read ALSA-Configuration.txt.\n"); return VIA_DXS_48K; }; @@ -2288,6 +2303,10 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci, chip->dxs_fixed = 1; else if (dxs_support[dev] == VIA_DXS_NO_VRA) chip->no_vra = 1; + else if (dxs_support[dev] == VIA_DXS_SRC) { + chip->no_vra = 1; + chip->dxs_src = 1; + } } if ((err = snd_via8233_init_misc(chip, dev)) < 0) goto __error; @@ -2334,7 +2353,7 @@ static struct pci_driver driver = { static int __init alsa_card_via82xx_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_via82xx_exit(void) diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ea5c6f6..5896d28 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -352,10 +352,8 @@ static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, snd_dma_free_pages(&dev->table); dev->table.area = NULL; } - if (dev->idx_table) { - kfree(dev->idx_table); - dev->idx_table = NULL; - } + kfree(dev->idx_table); + dev->idx_table = NULL; return 0; } @@ -420,7 +418,10 @@ static void snd_via82xx_codec_write(ac97_t *ac97, { via82xx_t *chip = ac97->private_data; unsigned int xval; - + if(reg == AC97_GPIO_STATUS) { + outl(val, VIAREG(chip, GPI_STATUS)); + return; + } xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; xval |= reg << VIA_REG_AC97_CMD_SHIFT; @@ -544,25 +545,6 @@ static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd) return 0; } -static int snd_via82xx_modem_pcm_trigger(snd_pcm_substream_t * substream, int cmd) -{ - via82xx_t *chip = snd_pcm_substream_chip(substream); - unsigned int val = 0; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS); - outl(val|AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS)); - break; - case SNDRV_PCM_TRIGGER_STOP: - val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS); - outl(val&~AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS)); - break; - default: - break; - } - return snd_via82xx_pcm_trigger(substream, cmd); -} - /* * pointer callbacks */ @@ -806,7 +788,7 @@ static snd_pcm_ops_t snd_via686_playback_ops = { .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, .prepare = snd_via82xx_pcm_prepare, - .trigger = snd_via82xx_modem_pcm_trigger, + .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, .page = snd_pcm_sgbuf_ops_page, }; @@ -819,7 +801,7 @@ static snd_pcm_ops_t snd_via686_capture_ops = { .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, .prepare = snd_via82xx_pcm_prepare, - .trigger = snd_via82xx_modem_pcm_trigger, + .trigger = snd_via82xx_pcm_trigger, .pointer = snd_via686_pcm_pointer, .page = snd_pcm_sgbuf_ops_page, }; @@ -938,7 +920,7 @@ static void __devinit snd_via82xx_proc_init(via82xx_t *chip) * */ -static int __devinit snd_via82xx_chip_init(via82xx_t *chip) +static int snd_via82xx_chip_init(via82xx_t *chip) { unsigned int val; int max_count; @@ -1233,7 +1215,7 @@ static struct pci_driver driver = { static int __init alsa_card_via82xx_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_via82xx_exit(void) diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 4ffbb25..dca6bd2 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -260,7 +260,7 @@ static struct pci_driver driver = { static int __init alsa_card_vx222_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_vx222_exit(void) diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 9f3ef22..5b5b624 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -360,7 +360,7 @@ static struct pci_driver driver = { static int __init alsa_card_ymfpci_init(void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void __exit alsa_card_ymfpci_exit(void) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 05f1629..2ae79610 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -829,9 +829,7 @@ static snd_pcm_hardware_t snd_ymfpci_capture = static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime) { - ymfpci_pcm_t *ypcm = runtime->private_data; - - kfree(ypcm); + kfree(runtime->private_data); } static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream) @@ -1421,17 +1419,15 @@ static snd_kcontrol_new_t snd_ymfpci_drec_source __devinitdata = { static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { - unsigned int mask = 1; - switch (kcontrol->private_value) { case YDSXGR_SPDIFOUTCTRL: break; case YDSXGR_SPDIFINCTRL: break; default: return -EINVAL; } - uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = 1; return 0; } @@ -1439,7 +1435,7 @@ static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value; - unsigned int shift = 0, mask = 1, invert = 0; + unsigned int shift = 0, mask = 1; switch (kcontrol->private_value) { case YDSXGR_SPDIFOUTCTRL: break; @@ -1447,8 +1443,6 @@ static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t default: return -EINVAL; } ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask; - if (invert) - ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0; } @@ -1456,7 +1450,7 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value; - unsigned int shift = 0, mask = 1, invert = 0; + unsigned int shift = 0, mask = 1; int change; unsigned int val, oval; @@ -1466,8 +1460,6 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t default: return -EINVAL; } val = (ucontrol->value.integer.value[0] & mask); - if (invert) - val = mask - val; val <<= shift; spin_lock_irq(&chip->reg_lock); oval = snd_ymfpci_readl(chip, reg); @@ -1487,14 +1479,13 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { unsigned int reg = kcontrol->private_value; - unsigned int mask = 16383; if (reg < 0x80 || reg >= 0xc0) return -EINVAL; - uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = 16383; return 0; } @@ -1502,7 +1493,7 @@ static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); unsigned int reg = kcontrol->private_value; - unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + unsigned int shift_left = 0, shift_right = 16, mask = 16383; unsigned int val; if (reg < 0x80 || reg >= 0xc0) @@ -1512,10 +1503,6 @@ static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t spin_unlock_irq(&chip->reg_lock); ucontrol->value.integer.value[0] = (val >> shift_left) & mask; ucontrol->value.integer.value[1] = (val >> shift_right) & mask; - if (invert) { - ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; - ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; - } return 0; } @@ -1523,7 +1510,7 @@ static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); unsigned int reg = kcontrol->private_value; - unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + unsigned int shift_left = 0, shift_right = 16, mask = 16383; int change; unsigned int val1, val2, oval; @@ -1531,10 +1518,6 @@ static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t return -EINVAL; val1 = ucontrol->value.integer.value[0] & mask; val2 = ucontrol->value.integer.value[1] & mask; - if (invert) { - val1 = mask - val1; - val2 = mask - val2; - } val1 <<= shift_left; val2 <<= shift_right; spin_lock_irq(&chip->reg_lock); |