From c0a9eedf9acafb083adf3ddbff0a1e4d6d9a6949 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:35 +0200 Subject: ALSA: ak4114 - fix errors in output selector bits * the previous version had a typo - values of AK4114_OPS10-12 were identical with AK4114_OPS00-02 * Since no cards actually use this feature, the bug was not identified earlier Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index d293d36..3ce69fd 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -95,13 +95,13 @@ /* AK4114_REG_IO0 */ #define AK4114_TX1E (1<<7) /* TX1 Output Enable (1 = enable) */ -#define AK4114_OPS12 (1<<2) /* Output Though Data Selector for TX1 pin */ -#define AK4114_OPS11 (1<<1) /* Output Though Data Selector for TX1 pin */ -#define AK4114_OPS10 (1<<0) /* Output Though Data Selector for TX1 pin */ +#define AK4114_OPS12 (1<<6) /* Output Data Selector for TX1 pin */ +#define AK4114_OPS11 (1<<5) /* Output Data Selector for TX1 pin */ +#define AK4114_OPS10 (1<<4) /* Output Data Selector for TX1 pin */ #define AK4114_TX0E (1<<3) /* TX0 Output Enable (1 = enable) */ -#define AK4114_OPS02 (1<<2) /* Output Though Data Selector for TX0 pin */ -#define AK4114_OPS01 (1<<1) /* Output Though Data Selector for TX0 pin */ -#define AK4114_OPS00 (1<<0) /* Output Though Data Selector for TX0 pin */ +#define AK4114_OPS02 (1<<2) /* Output Data Selector for TX0 pin */ +#define AK4114_OPS01 (1<<1) /* Output Data Selector for TX0 pin */ +#define AK4114_OPS00 (1<<0) /* Output Data Selector for TX0 pin */ /* AK4114_REG_IO1 */ #define AK4114_EFH1 (1<<7) /* Interrupt 0 pin Hold */ -- cgit v0.10.2 From 8f34692f63d66805b51ff408f4067748d3c1c3fd Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:36 +0200 Subject: ALSA: ak4620 support, codec regs listed in proc * complete support for ak4620 * codec regs listed in proc for all codecs/chips * adding total regs for each codec * fixing nb. of steps in input attenuation controls Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index 891cf1a..030b87c 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -68,7 +68,7 @@ struct snd_akm4xxx { enum { SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4358, SND_AK4381, - SND_AK5365 + SND_AK5365, SND_AK4620, } type; /* (array) information of combined codecs */ @@ -76,6 +76,9 @@ struct snd_akm4xxx { const struct snd_akm4xxx_adc_channel *adc_info; struct snd_ak4xxx_ops ops; + unsigned int num_chips; + unsigned int total_regs; + const char *name; }; void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index ee47aba..1adb8a3 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -19,7 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - */ + */ #include #include @@ -29,6 +29,7 @@ #include #include #include +#include MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters"); @@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write); static void ak4524_reset(struct snd_akm4xxx *ak, int state) { unsigned int chip; - unsigned char reg, maxreg; + unsigned char reg; - if (ak->type == SND_AK4528) - maxreg = 0x06; - else - maxreg = 0x08; for (chip = 0; chip < ak->num_dacs/2; chip++) { snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); if (state) continue; /* DAC volumes */ - for (reg = 0x04; reg < maxreg; reg++) + for (reg = 0x04; reg < ak->total_regs; reg++) snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg)); } } /* reset procedure for AK4355 and AK4358 */ -static void ak435X_reset(struct snd_akm4xxx *ak, int state, - unsigned char total_regs) +static void ak435X_reset(struct snd_akm4xxx *ak, int state) { unsigned char reg; @@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state, snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */ return; } - for (reg = 0x00; reg < total_regs; reg++) + for (reg = 0x00; reg < ak->total_regs; reg++) if (reg != 0x01) snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_get(ak, 0, reg)); @@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state) { unsigned int chip; unsigned char reg; - for (chip = 0; chip < ak->num_dacs/2; chip++) { snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); if (state) continue; - for (reg = 0x01; reg < 0x05; reg++) + for (reg = 0x01; reg < ak->total_regs; reg++) snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg)); } @@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) switch (ak->type) { case SND_AK4524: case SND_AK4528: + case SND_AK4620: ak4524_reset(ak, state); break; case SND_AK4529: /* FIXME: needed for ak4529? */ break; case SND_AK4355: - ak435X_reset(ak, state, 0x0b); + ak435X_reset(ak, state); break; case SND_AK4358: - ak435X_reset(ak, state, 0x10); + ak435X_reset(ak, state); break; case SND_AK4381: ak4381_reset(ak, state); @@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset); * Volume conversion table for non-linear volumes * from -63.5dB (mute) to 0dB step 0.5dB * - * Used for AK4524 input/ouput attenuation, AK4528, and + * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and * AK5365 input attenuation */ static const unsigned char vol_cvt_datt[128] = { @@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x00, 0x0f, /* 0: power-up, un-reset */ 0xff, 0xff }; + static const unsigned char inits_ak4620[] = { + 0x00, 0x07, /* 0: normal */ + 0x01, 0x00, /* 0: reset */ + 0x01, 0x02, /* 1: RSTAD */ + 0x01, 0x03, /* 1: RSTDA */ + 0x01, 0x0f, /* 1: normal */ + 0x02, 0x60, /* 2: 24bit I2S */ + 0x03, 0x01, /* 3: deemphasis off */ + 0x04, 0x00, /* 4: LIN muted */ + 0x05, 0x00, /* 5: RIN muted */ + 0x06, 0x00, /* 6: LOUT muted */ + 0x07, 0x00, /* 7: ROUT muted */ + 0xff, 0xff + }; - int chip, num_chips; + int chip; const unsigned char *ptr, *inits; unsigned char reg, data; @@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) switch (ak->type) { case SND_AK4524: inits = inits_ak4524; - num_chips = ak->num_dacs / 2; + ak->num_chips = ak->num_dacs / 2; + ak->name = "ak4524"; + ak->total_regs = 0x08; break; case SND_AK4528: inits = inits_ak4528; - num_chips = ak->num_dacs / 2; + ak->num_chips = ak->num_dacs / 2; + ak->name = "ak4528"; + ak->total_regs = 0x06; break; case SND_AK4529: inits = inits_ak4529; - num_chips = 1; + ak->num_chips = 1; + ak->name = "ak4529"; + ak->total_regs = 0x0d; break; case SND_AK4355: inits = inits_ak4355; - num_chips = 1; + ak->num_chips = 1; + ak->name = "ak4355"; + ak->total_regs = 0x0b; break; case SND_AK4358: inits = inits_ak4358; - num_chips = 1; + ak->num_chips = 1; + ak->name = "ak4358"; + ak->total_regs = 0x10; break; case SND_AK4381: inits = inits_ak4381; - num_chips = ak->num_dacs / 2; + ak->num_chips = ak->num_dacs / 2; + ak->name = "ak4381"; + ak->total_regs = 0x05; break; case SND_AK5365: /* FIXME: any init sequence? */ + ak->num_chips = 1; + ak->name = "ak5365"; + ak->total_regs = 0x08; return; + case SND_AK4620: + inits = inits_ak4620; + ak->num_chips = ak->num_dacs / 2; + ak->name = "ak4620"; + ak->total_regs = 0x08; + break; default: snd_BUG(); return; } - for (chip = 0; chip < num_chips; chip++) { + for (chip = 0; chip < ak->num_chips; chip++) { ptr = inits; while (*ptr != 0xff) { reg = *ptr++; data = *ptr++; snd_akm4xxx_write(ak, chip, reg, data); + udelay(10); } } } @@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak) AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); knew.tlv.p = db_scale_linear; break; + case SND_AK4620: + /* register 6 & 7 */ + knew.private_value = + AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255); + knew.tlv.p = db_scale_linear; + break; default: return -EINVAL; } @@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak) static int build_adc_controls(struct snd_akm4xxx *ak) { - int idx, err, mixer_ch, num_stereo; + int idx, err, mixer_ch, num_stereo, max_steps; struct snd_kcontrol_new knew; mixer_ch = 0; + if (ak->type == SND_AK4528) + return 0; /* no controls */ for (idx = 0; idx < ak->num_adcs;) { memset(&knew, 0, sizeof(knew)); if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) { @@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak) } /* register 4 & 5 */ if (ak->type == SND_AK5365) - knew.private_value = - AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) | - AK_VOL_CVT | AK_IPGA; + max_steps = 152; else - knew.private_value = - AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) | - AK_VOL_CVT | AK_IPGA; + max_steps = 164; + knew.private_value = + AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) | + AK_VOL_CVT | AK_IPGA; knew.tlv.p = db_scale_vol_datt; err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) @@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) switch (ak->type) { case SND_AK4524: case SND_AK4528: + case SND_AK4620: /* register 3 */ knew.private_value = AK_COMPOSE(idx, 3, 0, 0); break; @@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) return 0; } +#ifdef CONFIG_PROC_FS +static void proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data; + int reg, val, chip; + for (chip = 0; chip < ak->num_chips; chip++) { + for (reg = 0; reg < ak->total_regs; reg++) { + val = snd_akm4xxx_get(ak, chip, reg); + snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip, + reg, val); + } + } +} + +static int proc_init(struct snd_akm4xxx *ak) +{ + struct snd_info_entry *entry; + int err; + err = snd_card_proc_new(ak->card, ak->name, &entry); + if (err < 0) + return err; + snd_info_set_text_ops(entry, ak, proc_regs_read); + return 0; +} +#else /* !CONFIG_PROC_FS */ +static int proc_init(struct snd_akm4xxx *ak) {} +#endif + int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) { int err, num_emphs; @@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) err = build_adc_controls(ak); if (err < 0) return err; - if (ak->type == SND_AK4355 || ak->type == SND_AK4358) num_emphs = 1; + else if (ak->type == SND_AK4620) + num_emphs = 0; else num_emphs = ak->num_dacs / 2; err = build_deemphasis(ak, num_emphs); if (err < 0) return err; + err = proc_init(ak); + if (err < 0) + return err; return 0; } - EXPORT_SYMBOL(snd_akm4xxx_build_controls); static int __init alsa_akm4xxx_module_init(void) diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index fd948bf..4789e8b 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -412,25 +412,6 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = { }, }; - -static void ak4358_proc_regs_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; - int reg, val; - for (reg = 0; reg <= 0xf; reg++) { - val = snd_akm4xxx_get(ice->akm, 0, reg); - snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); - } -} - -static void ak4358_proc_init(struct snd_ice1712 *ice) -{ - struct snd_info_entry *entry; - if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry)) - snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read); -} - static char *slave_vols[] __devinitdata = { PCM_VOLUME, MONITOR_AN_IN_VOLUME, @@ -496,8 +477,6 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) /* only capture SPDIF over AK4114 */ err = snd_ak4114_build(spec->ak4114, NULL, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - - ak4358_proc_init(ice); if (err < 0) return err; return 0; -- cgit v0.10.2 From 42cfa276aebd28e5cc4350ff6c7d75f1cb84dd98 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:37 +0200 Subject: ALSA: ak4113 support * complete support for ak4113 * based on code for ak4114 and ak4117 Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/include/sound/ak4113.h b/include/sound/ak4113.h new file mode 100644 index 0000000..8988eda --- /dev/null +++ b/include/sound/ak4113.h @@ -0,0 +1,321 @@ +#ifndef __SOUND_AK4113_H +#define __SOUND_AK4113_H + +/* + * Routines for Asahi Kasei AK4113 + * Copyright (c) by Jaroslav Kysela , + * Copyright (c) by Pavel Hofman , + * + * + * 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 + * + */ + +/* AK4113 registers */ +/* power down */ +#define AK4113_REG_PWRDN 0x00 +/* format control */ +#define AK4113_REG_FORMAT 0x01 +/* input/output control */ +#define AK4113_REG_IO0 0x02 +/* input/output control */ +#define AK4113_REG_IO1 0x03 +/* interrupt0 mask */ +#define AK4113_REG_INT0_MASK 0x04 +/* interrupt1 mask */ +#define AK4113_REG_INT1_MASK 0x05 +/* DAT mask & DTS select */ +#define AK4113_REG_DATDTS 0x06 +/* receiver status 0 */ +#define AK4113_REG_RCS0 0x07 +/* receiver status 1 */ +#define AK4113_REG_RCS1 0x08 +/* receiver status 2 */ +#define AK4113_REG_RCS2 0x09 +/* RX channel status byte 0 */ +#define AK4113_REG_RXCSB0 0x0a +/* RX channel status byte 1 */ +#define AK4113_REG_RXCSB1 0x0b +/* RX channel status byte 2 */ +#define AK4113_REG_RXCSB2 0x0c +/* RX channel status byte 3 */ +#define AK4113_REG_RXCSB3 0x0d +/* RX channel status byte 4 */ +#define AK4113_REG_RXCSB4 0x0e +/* burst preamble Pc byte 0 */ +#define AK4113_REG_Pc0 0x0f +/* burst preamble Pc byte 1 */ +#define AK4113_REG_Pc1 0x10 +/* burst preamble Pd byte 0 */ +#define AK4113_REG_Pd0 0x11 +/* burst preamble Pd byte 1 */ +#define AK4113_REG_Pd1 0x12 +/* Q-subcode address + control */ +#define AK4113_REG_QSUB_ADDR 0x13 +/* Q-subcode track */ +#define AK4113_REG_QSUB_TRACK 0x14 +/* Q-subcode index */ +#define AK4113_REG_QSUB_INDEX 0x15 +/* Q-subcode minute */ +#define AK4113_REG_QSUB_MINUTE 0x16 +/* Q-subcode second */ +#define AK4113_REG_QSUB_SECOND 0x17 +/* Q-subcode frame */ +#define AK4113_REG_QSUB_FRAME 0x18 +/* Q-subcode zero */ +#define AK4113_REG_QSUB_ZERO 0x19 +/* Q-subcode absolute minute */ +#define AK4113_REG_QSUB_ABSMIN 0x1a +/* Q-subcode absolute second */ +#define AK4113_REG_QSUB_ABSSEC 0x1b +/* Q-subcode absolute frame */ +#define AK4113_REG_QSUB_ABSFRM 0x1c + +/* sizes */ +#define AK4113_REG_RXCSB_SIZE ((AK4113_REG_RXCSB4-AK4113_REG_RXCSB0)+1) +#define AK4113_REG_QSUB_SIZE ((AK4113_REG_QSUB_ABSFRM-AK4113_REG_QSUB_ADDR)\ + +1) + +#define AK4113_WRITABLE_REGS (AK4113_REG_DATDTS + 1) + +/* AK4113_REG_PWRDN bits */ +/* Channel Status Select */ +#define AK4113_CS12 (1<<7) +/* Block Start & C/U Output Mode */ +#define AK4113_BCU (1<<6) +/* Master Clock Operation Select */ +#define AK4113_CM1 (1<<5) +/* Master Clock Operation Select */ +#define AK4113_CM0 (1<<4) +/* Master Clock Frequency Select */ +#define AK4113_OCKS1 (1<<3) +/* Master Clock Frequency Select */ +#define AK4113_OCKS0 (1<<2) +/* 0 = power down, 1 = normal operation */ +#define AK4113_PWN (1<<1) +/* 0 = reset & initialize (except thisregister), 1 = normal operation */ +#define AK4113_RST (1<<0) + +/* AK4113_REQ_FORMAT bits */ +/* V/TX Output select: 0 = Validity Flag Output, 1 = TX */ +#define AK4113_VTX (1<<7) +/* Audio Data Control */ +#define AK4113_DIF2 (1<<6) +/* Audio Data Control */ +#define AK4113_DIF1 (1<<5) +/* Audio Data Control */ +#define AK4113_DIF0 (1<<4) +/* Deemphasis Autodetect Enable (1 = enable) */ +#define AK4113_DEAU (1<<3) +/* 32kHz-48kHz Deemphasis Control */ +#define AK4113_DEM1 (1<<2) +/* 32kHz-48kHz Deemphasis Control */ +#define AK4113_DEM0 (1<<1) +#define AK4113_DEM_OFF (AK4113_DEM0) +#define AK4113_DEM_44KHZ (0) +#define AK4113_DEM_48KHZ (AK4113_DEM1) +#define AK4113_DEM_32KHZ (AK4113_DEM0|AK4113_DEM1) +/* STDO: 16-bit, right justified */ +#define AK4113_DIF_16R (0) +/* STDO: 18-bit, right justified */ +#define AK4113_DIF_18R (AK4113_DIF0) +/* STDO: 20-bit, right justified */ +#define AK4113_DIF_20R (AK4113_DIF1) +/* STDO: 24-bit, right justified */ +#define AK4113_DIF_24R (AK4113_DIF1|AK4113_DIF0) +/* STDO: 24-bit, left justified */ +#define AK4113_DIF_24L (AK4113_DIF2) +/* STDO: I2S */ +#define AK4113_DIF_24I2S (AK4113_DIF2|AK4113_DIF0) +/* STDO: 24-bit, left justified; LRCLK, BICK = Input */ +#define AK4113_DIF_I24L (AK4113_DIF2|AK4113_DIF1) +/* STDO: I2S; LRCLK, BICK = Input */ +#define AK4113_DIF_I24I2S (AK4113_DIF2|AK4113_DIF1|AK4113_DIF0) + +/* AK4113_REG_IO0 */ +/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */ +#define AK4113_XTL1 (1<<6) +/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */ +#define AK4113_XTL0 (1<<5) +/* Block Start Signal Output: 0 = U-bit, 1 = C-bit (req. BCU = 1) */ +#define AK4113_UCE (1<<4) +/* TX Output Enable (1 = enable) */ +#define AK4113_TXE (1<<3) +/* Output Through Data Selector for TX pin */ +#define AK4113_OPS2 (1<<2) +/* Output Through Data Selector for TX pin */ +#define AK4113_OPS1 (1<<1) +/* Output Through Data Selector for TX pin */ +#define AK4113_OPS0 (1<<0) +/* 11.2896 MHz ref. Xtal freq. */ +#define AK4113_XTL_11_2896M (0) +/* 12.288 MHz ref. Xtal freq. */ +#define AK4113_XTL_12_288M (AK4113_XTL0) +/* 24.576 MHz ref. Xtal freq. */ +#define AK4113_XTL_24_576M (AK4113_XTL1) + +/* AK4113_REG_IO1 */ +/* Interrupt 0 pin Hold */ +#define AK4113_EFH1 (1<<7) +/* Interrupt 0 pin Hold */ +#define AK4113_EFH0 (1<<6) +#define AK4113_EFH_512LRCLK (0) +#define AK4113_EFH_1024LRCLK (AK4113_EFH0) +#define AK4113_EFH_2048LRCLK (AK4113_EFH1) +#define AK4113_EFH_4096LRCLK (AK4113_EFH1|AK4113_EFH0) +/* PLL Lock Time: 0 = 384/fs, 1 = 1/fs */ +#define AK4113_FAST (1<<5) +/* MCKO2 Output Select: 0 = CMx/OCKSx, 1 = Xtal */ +#define AK4113_XMCK (1<<4) +/* MCKO2 Output Freq. Select: 0 = x1, 1 = x0.5 (req. XMCK = 1) */ +#define AK4113_DIV (1<<3) +/* Input Recovery Data Select */ +#define AK4113_IPS2 (1<<2) +/* Input Recovery Data Select */ +#define AK4113_IPS1 (1<<1) +/* Input Recovery Data Select */ +#define AK4113_IPS0 (1<<0) +#define AK4113_IPS(x) ((x)&7) + +/* AK4113_REG_INT0_MASK && AK4113_REG_INT1_MASK*/ +/* mask enable for QINT bit */ +#define AK4113_MQI (1<<7) +/* mask enable for AUTO bit */ +#define AK4113_MAUT (1<<6) +/* mask enable for CINT bit */ +#define AK4113_MCIT (1<<5) +/* mask enable for UNLOCK bit */ +#define AK4113_MULK (1<<4) +/* mask enable for V bit */ +#define AK4113_V (1<<3) +/* mask enable for STC bit */ +#define AK4113_STC (1<<2) +/* mask enable for AUDN bit */ +#define AK4113_MAN (1<<1) +/* mask enable for PAR bit */ +#define AK4113_MPR (1<<0) + +/* AK4113_REG_DATDTS */ +/* DAT Start ID Counter */ +#define AK4113_DCNT (1<<4) +/* DTS-CD 16-bit Sync Word Detect */ +#define AK4113_DTS16 (1<<3) +/* DTS-CD 14-bit Sync Word Detect */ +#define AK4113_DTS14 (1<<2) +/* mask enable for DAT bit (if 1, no INT1 effect */ +#define AK4113_MDAT1 (1<<1) +/* mask enable for DAT bit (if 1, no INT0 effect */ +#define AK4113_MDAT0 (1<<0) + +/* AK4113_REG_RCS0 */ +/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */ +#define AK4113_QINT (1<<7) +/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */ +#define AK4113_AUTO (1<<6) +/* channel status buffer interrupt, 0 = no change, 1 = change */ +#define AK4113_CINT (1<<5) +/* PLL lock status, 0 = lock, 1 = unlock */ +#define AK4113_UNLCK (1<<4) +/* Validity bit, 0 = valid, 1 = invalid */ +#define AK4113_V (1<<3) +/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */ +#define AK4113_STC (1<<2) +/* audio bit output, 0 = audio, 1 = non-audio */ +#define AK4113_AUDION (1<<1) +/* parity error or biphase error status, 0 = no error, 1 = error */ +#define AK4113_PAR (1<<0) + +/* AK4113_REG_RCS1 */ +/* sampling frequency detection */ +#define AK4113_FS3 (1<<7) +#define AK4113_FS2 (1<<6) +#define AK4113_FS1 (1<<5) +#define AK4113_FS0 (1<<4) +/* Pre-emphasis detect, 0 = OFF, 1 = ON */ +#define AK4113_PEM (1<<3) +/* DAT Start ID Detect, 0 = no detect, 1 = detect */ +#define AK4113_DAT (1<<2) +/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */ +#define AK4113_DTSCD (1<<1) +/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */ +#define AK4113_NPCM (1<<0) +#define AK4113_FS_8000HZ (AK4113_FS3|AK4113_FS0) +#define AK4113_FS_11025HZ (AK4113_FS2|AK4113_FS0) +#define AK4113_FS_16000HZ (AK4113_FS2|AK4113_FS1|AK4113_FS0) +#define AK4113_FS_22050HZ (AK4113_FS2) +#define AK4113_FS_24000HZ (AK4113_FS2|AK4113_FS1) +#define AK4113_FS_32000HZ (AK4113_FS1|AK4113_FS0) +#define AK4113_FS_44100HZ (0) +#define AK4113_FS_48000HZ (AK4113_FS1) +#define AK4113_FS_64000HZ (AK4113_FS3|AK4113_FS1|AK4113_FS0) +#define AK4113_FS_88200HZ (AK4113_FS3) +#define AK4113_FS_96000HZ (AK4113_FS3|AK4113_FS1) +#define AK4113_FS_176400HZ (AK4113_FS3|AK4113_FS2) +#define AK4113_FS_192000HZ (AK4113_FS3|AK4113_FS2|AK4113_FS1) + +/* AK4113_REG_RCS2 */ +/* CRC for Q-subcode, 0 = no error, 1 = error */ +#define AK4113_QCRC (1<<1) +/* CRC for channel status, 0 = no error, 1 = error */ +#define AK4113_CCRC (1<<0) + +/* flags for snd_ak4113_check_rate_and_errors() */ +#define AK4113_CHECK_NO_STAT (1<<0) /* no statistics */ +#define AK4113_CHECK_NO_RATE (1<<1) /* no rate check */ + +#define AK4113_CONTROLS 13 + +typedef void (ak4113_write_t)(void *private_data, unsigned char addr, + unsigned char data); +typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr); + +struct ak4113 { + struct snd_card *card; + ak4113_write_t *write; + ak4113_read_t *read; + void *private_data; + unsigned int init:1; + spinlock_t lock; + unsigned char regmap[AK4113_WRITABLE_REGS]; + struct snd_kcontrol *kctls[AK4113_CONTROLS]; + struct snd_pcm_substream *substream; + unsigned long parity_errors; + unsigned long v_bit_errors; + unsigned long qcrc_errors; + unsigned long ccrc_errors; + unsigned char rcs0; + unsigned char rcs1; + unsigned char rcs2; + struct delayed_work work; + unsigned int check_flags; + void *change_callback_private; + void (*change_callback)(struct ak4113 *ak4113, unsigned char c0, + unsigned char c1); +}; + +int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, + ak4113_write_t *write, + const unsigned char pgm[AK4113_WRITABLE_REGS], + void *private_data, struct ak4113 **r_ak4113); +void snd_ak4113_reg_write(struct ak4113 *ak4113, unsigned char reg, + unsigned char mask, unsigned char val); +void snd_ak4113_reinit(struct ak4113 *ak4113); +int snd_ak4113_build(struct ak4113 *ak4113, + struct snd_pcm_substream *capture_substream); +int snd_ak4113_external_rate(struct ak4113 *ak4113); +int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags); + +#endif /* __SOUND_AK4113_H */ + diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile index 703d954..2dad40f 100644 --- a/sound/i2c/other/Makefile +++ b/sound/i2c/other/Makefile @@ -5,6 +5,7 @@ snd-ak4114-objs := ak4114.o snd-ak4117-objs := ak4117.o +snd-ak4113-objs := ak4113.o snd-ak4xxx-adda-objs := ak4xxx-adda.o snd-pt2258-objs := pt2258.o snd-tea575x-tuner-objs := tea575x-tuner.o @@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o # Module Dependency obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o -obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o +obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c new file mode 100644 index 0000000..fff62cc --- /dev/null +++ b/sound/i2c/other/ak4113.c @@ -0,0 +1,639 @@ +/* + * Routines for control of the AK4113 via I2C/4-wire serial interface + * IEC958 (S/PDIF) receiver by Asahi Kasei + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Pavel Hofman + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Pavel Hofman "); +MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei"); +MODULE_LICENSE("GPL"); + +#define AK4113_ADDR 0x00 /* fixed address */ + +static void ak4113_stats(struct work_struct *work); +static void ak4113_init_regs(struct ak4113 *chip); + + +static void reg_write(struct ak4113 *ak4113, unsigned char reg, + unsigned char val) +{ + ak4113->write(ak4113->private_data, reg, val); + if (reg < sizeof(ak4113->regmap)) + ak4113->regmap[reg] = val; +} + +static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg) +{ + return ak4113->read(ak4113->private_data, reg); +} + +static void snd_ak4113_free(struct ak4113 *chip) +{ + chip->init = 1; /* don't schedule new work */ + mb(); + cancel_delayed_work(&chip->work); + flush_scheduled_work(); + kfree(chip); +} + +static int snd_ak4113_dev_free(struct snd_device *device) +{ + struct ak4113 *chip = device->device_data; + snd_ak4113_free(chip); + return 0; +} + +int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, + ak4113_write_t *write, const unsigned char pgm[5], + void *private_data, struct ak4113 **r_ak4113) +{ + struct ak4113 *chip; + int err = 0; + unsigned char reg; + static struct snd_device_ops ops = { + .dev_free = snd_ak4113_dev_free, + }; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->lock); + chip->card = card; + chip->read = read; + chip->write = write; + chip->private_data = private_data; + INIT_DELAYED_WORK(&chip->work, ak4113_stats); + + for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++) + chip->regmap[reg] = pgm[reg]; + ak4113_init_regs(chip); + + chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT | + AK4113_CINT | AK4113_STC); + chip->rcs1 = reg_read(chip, AK4113_REG_RCS1); + chip->rcs2 = reg_read(chip, AK4113_REG_RCS2); + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) + goto __fail; + + if (r_ak4113) + *r_ak4113 = chip; + return 0; + +__fail: + snd_ak4113_free(chip); + return err < 0 ? err : -EIO; +} +EXPORT_SYMBOL_GPL(snd_ak4113_create); + +void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg >= AK4113_WRITABLE_REGS) + return; + reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); +} +EXPORT_SYMBOL_GPL(snd_ak4113_reg_write); + +static void ak4113_init_regs(struct ak4113 *chip) +{ + unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg; + + /* bring the chip to reset state and powerdown state */ + reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN)); + udelay(200); + /* release reset, but leave powerdown */ + reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN); + udelay(200); + for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++) + reg_write(chip, reg, chip->regmap[reg]); + /* release powerdown, everything is initialized now */ + reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN); +} + +void snd_ak4113_reinit(struct ak4113 *chip) +{ + chip->init = 1; + mb(); + flush_scheduled_work(); + ak4113_init_regs(chip); + /* bring up statistics / event queing */ + chip->init = 0; + if (chip->kctls[0]) + schedule_delayed_work(&chip->work, HZ / 10); +} +EXPORT_SYMBOL_GPL(snd_ak4113_reinit); + +static unsigned int external_rate(unsigned char rcs1) +{ + switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) { + case AK4113_FS_8000HZ: + return 8000; + case AK4113_FS_11025HZ: + return 11025; + case AK4113_FS_16000HZ: + return 16000; + case AK4113_FS_22050HZ: + return 22050; + case AK4113_FS_24000HZ: + return 24000; + case AK4113_FS_32000HZ: + return 32000; + case AK4113_FS_44100HZ: + return 44100; + case AK4113_FS_48000HZ: + return 48000; + case AK4113_FS_64000HZ: + return 64000; + case AK4113_FS_88200HZ: + return 88200; + case AK4113_FS_96000HZ: + return 96000; + case AK4113_FS_176400HZ: + return 176400; + case AK4113_FS_192000HZ: + return 192000; + default: + return 0; + } +} + +static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + return 0; +} + +static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + long *ptr; + + spin_lock_irq(&chip->lock); + ptr = (long *)(((char *)chip) + kcontrol->private_value); + ucontrol->value.integer.value[0] = *ptr; + *ptr = 0; + spin_unlock_irq(&chip->lock); + return 0; +} + +#define snd_ak4113_in_bit_info snd_ctl_boolean_mono_info + +static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + unsigned char reg = kcontrol->private_value & 0xff; + unsigned char bit = (kcontrol->private_value >> 8) & 0xff; + unsigned char inv = (kcontrol->private_value >> 31) & 1; + + ucontrol->value.integer.value[0] = + ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv; + return 0; +} + +static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 5; + return 0; +} + +static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + (AK4113_IPS(chip->regmap[AK4113_REG_IO1])); + return 0; +} + +static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + int change; + u8 old_val; + + spin_lock_irq(&chip->lock); + old_val = chip->regmap[AK4113_REG_IO1]; + change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val); + if (change) + reg_write(chip, AK4113_REG_IO1, + (old_val & (~AK4113_IPS(0xff))) | + (AK4113_IPS(ucontrol->value.integer.value[0]))); + spin_unlock_irq(&chip->lock); + return change; +} + +static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + return 0; +} + +static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = external_rate(reg_read(chip, + AK4113_REG_RCS1)); + return 0; +} + +static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++) + ucontrol->value.iec958.status[i] = reg_read(chip, + AK4113_REG_RXCSB0 + i); + return 0; +} + +static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE); + return 0; +} + +static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + uinfo->count = 4; + return 0; +} + +static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + unsigned short tmp; + + ucontrol->value.integer.value[0] = 0xf8f2; + ucontrol->value.integer.value[1] = 0x4e1f; + tmp = reg_read(chip, AK4113_REG_Pc0) | + (reg_read(chip, AK4113_REG_Pc1) << 8); + ucontrol->value.integer.value[2] = tmp; + tmp = reg_read(chip, AK4113_REG_Pd0) | + (reg_read(chip, AK4113_REG_Pd1) << 8); + ucontrol->value.integer.value[3] = tmp; + return 0; +} + +static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = AK4113_REG_QSUB_SIZE; + return 0; +} + +static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ak4113 *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4113_REG_QSUB_SIZE; i++) + ucontrol->value.bytes.data[i] = reg_read(chip, + AK4113_REG_QSUB_ADDR + i); + return 0; +} + +/* Don't forget to change AK4113_CONTROLS define!!! */ +static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Parity Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_error_info, + .get = snd_ak4113_in_error_get, + .private_value = offsetof(struct ak4113, parity_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 V-Bit Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_error_info, + .get = snd_ak4113_in_error_get, + .private_value = offsetof(struct ak4113, v_bit_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 C-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_error_info, + .get = snd_ak4113_in_error_get, + .private_value = offsetof(struct ak4113, ccrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_error_info, + .get = snd_ak4113_in_error_get, + .private_value = offsetof(struct ak4113, qcrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 External Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_rate_info, + .get = snd_ak4113_rate_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ak4113_spdif_mask_info, + .get = snd_ak4113_spdif_mask_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_spdif_info, + .get = snd_ak4113_spdif_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Preample Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_spdif_pinfo, + .get = snd_ak4113_spdif_pget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_spdif_qinfo, + .get = snd_ak4113_spdif_qget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Audio", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_bit_info, + .get = snd_ak4113_in_bit_get, + .private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Non-PCM Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_bit_info, + .get = snd_ak4113_in_bit_get, + .private_value = (0<<8) | AK4113_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 DTS Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4113_in_bit_info, + .get = snd_ak4113_in_bit_get, + .private_value = (1<<8) | AK4113_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "AK4113 Input Select", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_ak4113_rx_info, + .get = snd_ak4113_rx_get, + .put = snd_ak4113_rx_put, +} +}; + +static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct ak4113 *ak4113 = entry->private_data; + int reg, val; + /* all ak4113 registers 0x00 - 0x1c */ + for (reg = 0; reg < 0x1d; reg++) { + val = reg_read(ak4113, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); + } +} + +static void snd_ak4113_proc_init(struct ak4113 *ak4113) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ak4113->card, "ak4113", &entry)) + snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read); +} + +int snd_ak4113_build(struct ak4113 *ak4113, + struct snd_pcm_substream *cap_substream) +{ + struct snd_kcontrol *kctl; + unsigned int idx; + int err; + + if (snd_BUG_ON(!cap_substream)) + return -EINVAL; + ak4113->substream = cap_substream; + for (idx = 0; idx < AK4113_CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = cap_substream->pcm->device; + kctl->id.subdevice = cap_substream->number; + err = snd_ctl_add(ak4113->card, kctl); + if (err < 0) + return err; + ak4113->kctls[idx] = kctl; + } + snd_ak4113_proc_init(ak4113); + /* trigger workq */ + schedule_delayed_work(&ak4113->work, HZ / 10); + return 0; +} +EXPORT_SYMBOL_GPL(snd_ak4113_build); + +int snd_ak4113_external_rate(struct ak4113 *ak4113) +{ + unsigned char rcs1; + + rcs1 = reg_read(ak4113, AK4113_REG_RCS1); + return external_rate(rcs1); +} +EXPORT_SYMBOL_GPL(snd_ak4113_external_rate); + +int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags) +{ + struct snd_pcm_runtime *runtime = + ak4113->substream ? ak4113->substream->runtime : NULL; + unsigned long _flags; + int res = 0; + unsigned char rcs0, rcs1, rcs2; + unsigned char c0, c1; + + rcs1 = reg_read(ak4113, AK4113_REG_RCS1); + if (flags & AK4113_CHECK_NO_STAT) + goto __rate; + rcs0 = reg_read(ak4113, AK4113_REG_RCS0); + rcs2 = reg_read(ak4113, AK4113_REG_RCS2); + spin_lock_irqsave(&ak4113->lock, _flags); + if (rcs0 & AK4113_PAR) + ak4113->parity_errors++; + if (rcs0 & AK4113_V) + ak4113->v_bit_errors++; + if (rcs2 & AK4113_CCRC) + ak4113->ccrc_errors++; + if (rcs2 & AK4113_QCRC) + ak4113->qcrc_errors++; + c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC | + AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^ + (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC | + AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)); + c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM | + AK4113_DAT | 0xf0)) ^ + (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM | + AK4113_DAT | 0xf0)); + ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC); + ak4113->rcs1 = rcs1; + ak4113->rcs2 = rcs2; + spin_unlock_irqrestore(&ak4113->lock, _flags); + + if (rcs0 & AK4113_PAR) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[0]->id); + if (rcs0 & AK4113_V) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[1]->id); + if (rcs2 & AK4113_CCRC) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[2]->id); + if (rcs2 & AK4113_QCRC) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[3]->id); + + /* rate change */ + if (c1 & 0xf0) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[4]->id); + + if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT)) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[6]->id); + if (c0 & AK4113_QINT) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[8]->id); + + if (c0 & AK4113_AUDION) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[9]->id); + if (c1 & AK4113_NPCM) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[10]->id); + if (c1 & AK4113_DTSCD) + snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE, + &ak4113->kctls[11]->id); + + if (ak4113->change_callback && (c0 | c1) != 0) + ak4113->change_callback(ak4113, c0, c1); + +__rate: + /* compare rate */ + res = external_rate(rcs1); + if (!(flags & AK4113_CHECK_NO_RATE) && runtime && + (runtime->rate != res)) { + snd_pcm_stream_lock_irqsave(ak4113->substream, _flags); + if (snd_pcm_running(ak4113->substream)) { + /*printk(KERN_DEBUG "rate changed (%i <- %i)\n", + * runtime->rate, res); */ + snd_pcm_stop(ak4113->substream, + SNDRV_PCM_STATE_DRAINING); + wake_up(&runtime->sleep); + res = 1; + } + snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags); + } + return res; +} +EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors); + +static void ak4113_stats(struct work_struct *work) +{ + struct ak4113 *chip = container_of(work, struct ak4113, work.work); + + if (!chip->init) + snd_ak4113_check_rate_and_errors(chip, chip->check_flags); + + schedule_delayed_work(&chip->work, HZ / 10); +} -- cgit v0.10.2 From 494703062b6e6ef5e72364aafc9bcbc172d53dea Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:38 +0200 Subject: ALSA: ice1724 - adding GPIO routines for mask and direction * get/set routines for GPIO mask and direction Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index cecf1ff..56d8d67 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -298,6 +298,16 @@ static void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data) inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */ } +static unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice) +{ + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION); +} + +static unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice) +{ + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK); +} + static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) { snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data); @@ -2557,7 +2567,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card, mutex_init(&ice->i2c_mutex); mutex_init(&ice->open_mutex); ice->gpio.set_mask = snd_ice1712_set_gpio_mask; + ice->gpio.get_mask = snd_ice1712_get_gpio_mask; ice->gpio.set_dir = snd_ice1712_set_gpio_dir; + ice->gpio.get_dir = snd_ice1712_get_gpio_dir; ice->gpio.set_data = snd_ice1712_set_gpio_data; ice->gpio.get_data = snd_ice1712_get_gpio_data; diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 9da2dae..b31a59d 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -359,7 +359,9 @@ struct snd_ice1712 { unsigned int saved[2]; /* for ewx_i2c */ /* operators */ void (*set_mask)(struct snd_ice1712 *ice, unsigned int data); + unsigned int (*get_mask)(struct snd_ice1712 *ice); void (*set_dir)(struct snd_ice1712 *ice, unsigned int data); + unsigned int (*get_dir)(struct snd_ice1712 *ice); void (*set_data)(struct snd_ice1712 *ice, unsigned int data); unsigned int (*get_data)(struct snd_ice1712 *ice); /* misc operators - move to another place? */ @@ -399,6 +401,11 @@ static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned in ice->gpio.set_dir(ice, bits); } +static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice) +{ + return ice->gpio.get_dir(ice); +} + static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits) { ice->gpio.set_mask(ice, bits); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index af6e001..2213bee 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -196,6 +196,12 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data) inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */ } +/* get gpio direction 0 = read, 1 = write */ +static unsigned int snd_vt1724_get_gpio_dir(struct snd_ice1712 *ice) +{ + return inl(ICEREG1724(ice, GPIO_DIRECTION)); +} + /* set the gpio mask (0 = writable) */ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) { @@ -205,6 +211,17 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */ } +static unsigned int snd_vt1724_get_gpio_mask(struct snd_ice1712 *ice) +{ + unsigned int mask; + if (!ice->vt1720) + mask = (unsigned int)inb(ICEREG1724(ice, GPIO_WRITE_MASK_22)); + else + mask = 0; + mask = (mask << 16) | inw(ICEREG1724(ice, GPIO_WRITE_MASK)); + return mask; +} + static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data) { outw(data, ICEREG1724(ice, GPIO_DATA)); @@ -2434,7 +2451,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card, mutex_init(&ice->open_mutex); mutex_init(&ice->i2c_mutex); ice->gpio.set_mask = snd_vt1724_set_gpio_mask; + ice->gpio.get_mask = snd_vt1724_get_gpio_mask; ice->gpio.set_dir = snd_vt1724_set_gpio_dir; + ice->gpio.get_dir = snd_vt1724_get_gpio_dir; ice->gpio.set_data = snd_vt1724_set_gpio_data; ice->gpio.get_data = snd_vt1724_get_gpio_data; ice->card = card; -- cgit v0.10.2 From 6796d5a05f4d3caad17d2586b3e5776fda50ef82 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:39 +0200 Subject: ALSA: ice1724 - pro-rate-locking makes sense only for internal clock mode * pro-rate-locking applies to internal clock mode only * required rate and current rate are compared for internal clock mode only Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 2213bee..514e153 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -118,9 +118,12 @@ static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice) return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0; } +/* + * locking rate makes sense only for internal clock mode + */ static inline int is_pro_rate_locked(struct snd_ice1712 *ice) { - return ice->is_spdif_master(ice) || PRO_RATE_LOCKED; + return (!ice->is_spdif_master(ice)) && PRO_RATE_LOCKED; } /* @@ -668,16 +671,22 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, return -EBUSY; } if (!force && is_pro_rate_locked(ice)) { + /* comparing required and current rate - makes sense for + * internal clock only */ spin_unlock_irqrestore(&ice->reg_lock, flags); return (rate == ice->cur_rate) ? 0 : -EBUSY; } - old_rate = ice->get_rate(ice); - if (force || (old_rate != rate)) - ice->set_rate(ice, rate); - else if (rate == ice->cur_rate) { - spin_unlock_irqrestore(&ice->reg_lock, flags); - return 0; + if (force || !ice->is_spdif_master(ice)) { + /* force means the rate was switched by ucontrol, otherwise + * setting clock rate for internal clock mode */ + old_rate = ice->get_rate(ice); + if (force || (old_rate != rate)) + ice->set_rate(ice, rate); + else if (rate == ice->cur_rate) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; + } } ice->cur_rate = rate; -- cgit v0.10.2 From 1ff97cb9dd9f53b33ce6710a4f861f43e70e8ca4 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:40 +0200 Subject: ALSA: ice1724 - Support for multiple external clock types * Support for customization of the external clock names * Adding hooks to playback_pro_open and capture_pro_open, allowing e.g. limiting available stream rates to a single value when the external clock rate is detected Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index b31a59d..4615bca 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -379,8 +379,11 @@ struct snd_ice1712 { unsigned int (*get_rate)(struct snd_ice1712 *ice); void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate); unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate); - void (*set_spdif_clock)(struct snd_ice1712 *ice); - + int (*set_spdif_clock)(struct snd_ice1712 *ice, int type); + int (*get_spdif_master_type)(struct snd_ice1712 *ice); + char **ext_clock_names; + int ext_clock_count; + void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *); #ifdef CONFIG_PM int (*pm_suspend)(struct snd_ice1712 *); int (*pm_resume)(struct snd_ice1712 *); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 514e153..3f11195 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -104,6 +104,8 @@ static int PRO_RATE_LOCKED; static int PRO_RATE_RESET = 1; static unsigned int PRO_RATE_DEFAULT = 44100; +static char *ext_clock_names[1] = { "IEC958 In" }; + /* * Basic I/O */ @@ -1042,6 +1044,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->pro_open) + ice->pro_open(ice, substream); return 0; } @@ -1060,6 +1064,8 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->pro_open) + ice->pro_open(ice, substream); return 0; } @@ -1813,15 +1819,21 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - + int hw_rates_count = ice->hw_rates->count; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = ice->hw_rates->count + 1; + + uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count; + /* upper limit - keep at top */ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1) - strcpy(uinfo->value.enumerated.name, "IEC958 Input"); + if (uinfo->value.enumerated.item >= hw_rates_count) + /* ext_clock items */ + strcpy(uinfo->value.enumerated.name, + ice->ext_clock_names[ + uinfo->value.enumerated.item - hw_rates_count]); else + /* int clock items */ sprintf(uinfo->value.enumerated.name, "%d", ice->hw_rates->list[uinfo->value.enumerated.item]); return 0; @@ -1835,7 +1847,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, spin_lock_irq(&ice->reg_lock); if (ice->is_spdif_master(ice)) { - ucontrol->value.enumerated.item[0] = ice->hw_rates->count; + ucontrol->value.enumerated.item[0] = ice->hw_rates->count + + ice->get_spdif_master_type(ice); } else { rate = ice->get_rate(ice); ucontrol->value.enumerated.item[0] = 0; @@ -1850,8 +1863,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, return 0; } +static int stdclock_get_spdif_master_type(struct snd_ice1712 *ice) +{ + /* standard external clock - only single type - SPDIF IN */ + return 0; +} + /* setting clock to external - SPDIF */ -static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) +static int stdclock_set_spdif_clock(struct snd_ice1712 *ice, int type) { unsigned char oval; unsigned char i2s_oval; @@ -1860,27 +1879,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) /* setting 256fs */ i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT)); + return 0; } + static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); unsigned int old_rate, new_rate; unsigned int item = ucontrol->value.enumerated.item[0]; - unsigned int spdif = ice->hw_rates->count; + unsigned int first_ext_clock = ice->hw_rates->count; - if (item > spdif) + if (item > first_ext_clock + ice->ext_clock_count - 1) return -EINVAL; + /* if rate = 0 => external clock */ spin_lock_irq(&ice->reg_lock); if (ice->is_spdif_master(ice)) old_rate = 0; else old_rate = ice->get_rate(ice); - if (item == spdif) { - /* switching to external clock via SPDIF */ - ice->set_spdif_clock(ice); + if (item >= first_ext_clock) { + /* switching to external clock */ + ice->set_spdif_clock(ice, item - first_ext_clock); new_rate = 0; } else { /* internal on-card clock */ @@ -1892,7 +1914,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, } spin_unlock_irq(&ice->reg_lock); - /* the first reset to the SPDIF master mode? */ + /* the first switch to the ext. clock mode? */ if (old_rate != new_rate && !new_rate) { /* notify akm chips as well */ unsigned int i; @@ -2550,6 +2572,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, return err; } + /* field init before calling chip_init */ + ice->ext_clock_count = 0; + for (tbl = card_tables; *tbl; tbl++) { for (c = *tbl; c->subvendor; c++) { if (c->subvendor == ice->eeprom.subvendor) { @@ -2588,6 +2613,13 @@ __found: ice->set_mclk = stdclock_set_mclk; if (!ice->set_spdif_clock) ice->set_spdif_clock = stdclock_set_spdif_clock; + if (!ice->get_spdif_master_type) + ice->get_spdif_master_type = stdclock_get_spdif_master_type; + if (!ice->ext_clock_names) + ice->ext_clock_names = ext_clock_names; + if (!ice->ext_clock_count) + ice->ext_clock_count = ARRAY_SIZE(ext_clock_names); + if (!ice->hw_rates) set_std_hw_rates(ice); @@ -2747,7 +2779,7 @@ static int snd_vt1724_resume(struct pci_dev *pci) if (ice->pm_saved_is_spdif_master) { /* switching to external clock via SPDIF */ - ice->set_spdif_clock(ice); + ice->set_spdif_clock(ice, 0); } else { /* internal on-card clock */ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 4789e8b..4bed963 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -529,13 +529,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, } /* setting clock to external - SPDIF */ -static void juli_set_spdif_clock(struct snd_ice1712 *ice) +static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type) { unsigned int old; old = ice->gpio.get_data(ice); /* external clock (= 0), multiply 1x, 48kHz */ ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | GPIO_FREQ_48KHZ); + return 0; } /* Called when ak4114 detects change in the input SPDIF stream */ -- cgit v0.10.2 From 6ef80706184be792499a4485a7957f2660b6a076 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:41 +0200 Subject: ALSA: ice1724 - Infrasonic Quartet support * three external clock types * all controls supported Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 536eae2..f7ce33f 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 3f11195..3896fb9 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -53,6 +53,7 @@ #include "phase.h" #include "wtm.h" #include "se.h" +#include "quartet.h" MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); @@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{" PHASE_DEVICE_DESC WTM_DEVICE_DESC SE_DEVICE_DESC + QTET_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -2184,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_phase_cards, snd_vt1724_wtm_cards, snd_vt1724_se_cards, + snd_vt1724_qtet_cards, NULL, }; diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c new file mode 100644 index 0000000..1948632 --- /dev/null +++ b/sound/pci/ice1712/quartet.c @@ -0,0 +1,1130 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Infrasonic Quartet + * + * Copyright (c) 2009 Pavel Hofman + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "envy24ht.h" +#include +#include "quartet.h" + +struct qtet_spec { + struct ak4113 *ak4113; + unsigned int scr; /* system control register */ + unsigned int mcr; /* monitoring control register */ + unsigned int cpld; /* cpld register */ +}; + +struct qtet_kcontrol_private { + unsigned int bit; + void (*set_register)(struct snd_ice1712 *ice, unsigned int val); + unsigned int (*get_register)(struct snd_ice1712 *ice); + unsigned char *texts[2]; +}; + +enum { + IN12_SEL = 0, + IN34_SEL, + AIN34_SEL, + COAX_OUT, + IN12_MON12, + IN12_MON34, + IN34_MON12, + IN34_MON34, + OUT12_MON34, + OUT34_MON12, +}; + +static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS", + "Word Clock 256xFS"}; + +/* chip address on I2C bus */ +#define AK4113_ADDR 0x26 /* S/PDIF receiver */ + +/* chip address on SPI bus */ +#define AK4620_ADDR 0x02 /* ADC/DAC */ + + +/* + * GPIO pins + */ + +/* GPIO0 - O - DATA0, def. 0 */ +#define GPIO_D0 (1<<0) +/* GPIO1 - I/O - DATA1, Jack Detect Input0 (0:present, 1:missing), def. 1 */ +#define GPIO_D1_JACKDTC0 (1<<1) +/* GPIO2 - I/O - DATA2, Jack Detect Input1 (0:present, 1:missing), def. 1 */ +#define GPIO_D2_JACKDTC1 (1<<2) +/* GPIO3 - I/O - DATA3, def. 1 */ +#define GPIO_D3 (1<<3) +/* GPIO4 - I/O - DATA4, SPI CDTO, def. 1 */ +#define GPIO_D4_SPI_CDTO (1<<4) +/* GPIO5 - I/O - DATA5, SPI CCLK, def. 1 */ +#define GPIO_D5_SPI_CCLK (1<<5) +/* GPIO6 - I/O - DATA6, Cable Detect Input (0:detected, 1:not detected */ +#define GPIO_D6_CD (1<<6) +/* GPIO7 - I/O - DATA7, Device Detect Input (0:detected, 1:not detected */ +#define GPIO_D7_DD (1<<7) +/* GPIO8 - O - CPLD Chip Select, def. 1 */ +#define GPIO_CPLD_CSN (1<<8) +/* GPIO9 - O - CPLD register read/write (0:write, 1:read), def. 0 */ +#define GPIO_CPLD_RW (1<<9) +/* GPIO10 - O - SPI Chip Select for CODEC#0, def. 1 */ +#define GPIO_SPI_CSN0 (1<<10) +/* GPIO11 - O - SPI Chip Select for CODEC#1, def. 1 */ +#define GPIO_SPI_CSN1 (1<<11) +/* GPIO12 - O - Ex. Register Output Enable (0:enable, 1:disable), def. 1, + * init 0 */ +#define GPIO_EX_GPIOE (1<<12) +/* GPIO13 - O - Ex. Register0 Chip Select for System Control Register, + * def. 1 */ +#define GPIO_SCR (1<<13) +/* GPIO14 - O - Ex. Register1 Chip Select for Monitor Control Register, + * def. 1 */ +#define GPIO_MCR (1<<14) + +#define GPIO_SPI_ALL (GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK |\ + GPIO_SPI_CSN0 | GPIO_SPI_CSN1) + +#define GPIO_DATA_MASK (GPIO_D0 | GPIO_D1_JACKDTC0 | \ + GPIO_D2_JACKDTC1 | GPIO_D3 | \ + GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK | \ + GPIO_D6_CD | GPIO_D7_DD) + +/* System Control Register GPIO_SCR data bits */ +/* Mic/Line select relay (0:line, 1:mic) */ +#define SCR_RELAY GPIO_D0 +/* Phantom power drive control (0:5V, 1:48V) */ +#define SCR_PHP_V GPIO_D1_JACKDTC0 +/* H/W mute control (0:Normal, 1:Mute) */ +#define SCR_MUTE GPIO_D2_JACKDTC1 +/* Phantom power control (0:Phantom on, 1:off) */ +#define SCR_PHP GPIO_D3 +/* Analog input 1/2 Source Select */ +#define SCR_AIN12_SEL0 GPIO_D4_SPI_CDTO +#define SCR_AIN12_SEL1 GPIO_D5_SPI_CCLK +/* Analog input 3/4 Source Select (0:line, 1:hi-z) */ +#define SCR_AIN34_SEL GPIO_D6_CD +/* Codec Power Down (0:power down, 1:normal) */ +#define SCR_CODEC_PDN GPIO_D7_DD + +#define SCR_AIN12_LINE (0) +#define SCR_AIN12_MIC (SCR_AIN12_SEL0) +#define SCR_AIN12_LOWCUT (SCR_AIN12_SEL1 | SCR_AIN12_SEL0) + +/* Monitor Control Register GPIO_MCR data bits */ +/* Input 1/2 to Monitor 1/2 (0:off, 1:on) */ +#define MCR_IN12_MON12 GPIO_D0 +/* Input 1/2 to Monitor 3/4 (0:off, 1:on) */ +#define MCR_IN12_MON34 GPIO_D1_JACKDTC0 +/* Input 3/4 to Monitor 1/2 (0:off, 1:on) */ +#define MCR_IN34_MON12 GPIO_D2_JACKDTC1 +/* Input 3/4 to Monitor 3/4 (0:off, 1:on) */ +#define MCR_IN34_MON34 GPIO_D3 +/* Output to Monitor 1/2 (0:off, 1:on) */ +#define MCR_OUT34_MON12 GPIO_D4_SPI_CDTO +/* Output to Monitor 3/4 (0:off, 1:on) */ +#define MCR_OUT12_MON34 GPIO_D5_SPI_CCLK + +/* CPLD Register DATA bits */ +/* Clock Rate Select */ +#define CPLD_CKS0 GPIO_D0 +#define CPLD_CKS1 GPIO_D1_JACKDTC0 +#define CPLD_CKS2 GPIO_D2_JACKDTC1 +/* Sync Source Select (0:Internal, 1:External) */ +#define CPLD_SYNC_SEL GPIO_D3 +/* Word Clock FS Select (0:FS, 1:256FS) */ +#define CPLD_WORD_SEL GPIO_D4_SPI_CDTO +/* Coaxial Output Source (IS-Link) (0:SPDIF, 1:I2S) */ +#define CPLD_COAX_OUT GPIO_D5_SPI_CCLK +/* Input 1/2 Source Select (0:Analog12, 1:An34) */ +#define CPLD_IN12_SEL GPIO_D6_CD +/* Input 3/4 Source Select (0:Analog34, 1:Digital In) */ +#define CPLD_IN34_SEL GPIO_D7_DD + +/* internal clock (CPLD_SYNC_SEL = 0) options */ +#define CPLD_CKS_44100HZ (0) +#define CPLD_CKS_48000HZ (CPLD_CKS0) +#define CPLD_CKS_88200HZ (CPLD_CKS1) +#define CPLD_CKS_96000HZ (CPLD_CKS1 | CPLD_CKS0) +#define CPLD_CKS_176400HZ (CPLD_CKS2) +#define CPLD_CKS_192000HZ (CPLD_CKS2 | CPLD_CKS0) + +#define CPLD_CKS_MASK (CPLD_CKS0 | CPLD_CKS1 | CPLD_CKS2) + +/* external clock (CPLD_SYNC_SEL = 1) options */ +/* external clock - SPDIF */ +#define CPLD_EXT_SPDIF (0 | CPLD_SYNC_SEL) +/* external clock - WordClock 1xfs */ +#define CPLD_EXT_WORDCLOCK_1FS (CPLD_CKS1 | CPLD_SYNC_SEL) +/* external clock - WordClock 256xfs */ +#define CPLD_EXT_WORDCLOCK_256FS (CPLD_CKS1 | CPLD_WORD_SEL |\ + CPLD_SYNC_SEL) + +#define EXT_SPDIF_TYPE 0 +#define EXT_WORDCLOCK_1FS_TYPE 1 +#define EXT_WORDCLOCK_256FS_TYPE 2 + +#define AK4620_DFS0 (1<<0) +#define AK4620_DFS1 (1<<1) +#define AK4620_CKS0 (1<<2) +#define AK4620_CKS1 (1<<3) +/* Clock and Format Control register */ +#define AK4620_DFS_REG 0x02 + +/* Deem and Volume Control register */ +#define AK4620_DEEMVOL_REG 0x03 +#define AK4620_SMUTE (1<<7) + +/* + * Conversion from int value to its binary form. Used for debugging. + * The output buffer must be allocated prior to calling the function. + */ +static char *get_binary(char *buffer, int value) +{ + int i, j, pos; + pos = 0; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 8; ++j) { + if (value & (1 << (31-(i*8 + j)))) + buffer[pos] = '1'; + else + buffer[pos] = '0'; + pos++; + } + if (i < 3) { + buffer[pos] = ' '; + pos++; + } + } + buffer[pos] = '\0'; + return buffer; +} + +/* + * Initial setup of the conversion array GPIO <-> rate + */ +static unsigned int qtet_rates[] = { + 44100, 48000, 88200, + 96000, 176400, 192000, +}; + +static unsigned int cks_vals[] = { + CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ, + CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ, +}; + +static struct snd_pcm_hw_constraint_list qtet_rates_info = { + .count = ARRAY_SIZE(qtet_rates), + .list = qtet_rates, + .mask = 0, +}; + +static void qtet_ak4113_write(void *private_data, unsigned char reg, + unsigned char val) +{ + snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4113_ADDR, + reg, val); +} + +static unsigned char qtet_ak4113_read(void *private_data, unsigned char reg) +{ + return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, + AK4113_ADDR, reg); +} + + +/* + * AK4620 section + */ + +/* + * Write data to addr register of ak4620 + */ +static void qtet_akm_write(struct snd_akm4xxx *ak, int chip, + unsigned char addr, unsigned char data) +{ + unsigned int tmp, orig_dir; + int idx; + unsigned int addrdata; + struct snd_ice1712 *ice = ak->private_data[0]; + + if (snd_BUG_ON(chip < 0 || chip >= 4)) + return; + /*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x, + data=0x%x\n", chip, addr, data);*/ + orig_dir = ice->gpio.get_dir(ice); + ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL); + /* set mask - only SPI bits */ + ice->gpio.set_mask(ice, ~GPIO_SPI_ALL); + + tmp = ice->gpio.get_data(ice); + /* high all */ + tmp |= GPIO_SPI_ALL; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop chip select */ + if (chip) + /* CODEC 1 */ + tmp &= ~GPIO_SPI_CSN1; + else + tmp &= ~GPIO_SPI_CSN0; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* build I2C address + data byte */ + addrdata = (AK4620_ADDR << 6) | 0x20 | (addr & 0x1f); + addrdata = (addrdata << 8) | data; + for (idx = 15; idx >= 0; idx--) { + /* drop clock */ + tmp &= ~GPIO_D5_SPI_CCLK; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* set data */ + if (addrdata & (1 << idx)) + tmp |= GPIO_D4_SPI_CDTO; + else + tmp &= ~GPIO_D4_SPI_CDTO; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* raise clock */ + tmp |= GPIO_D5_SPI_CCLK; + ice->gpio.set_data(ice, tmp); + udelay(100); + } + /* all back to 1 */ + tmp |= GPIO_SPI_ALL; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* return all gpios to non-writable */ + ice->gpio.set_mask(ice, 0xffffff); + /* restore GPIOs direction */ + ice->gpio.set_dir(ice, orig_dir); +} + +static void qtet_akm_set_regs(struct snd_akm4xxx *ak, unsigned char addr, + unsigned char mask, unsigned char value) +{ + unsigned char tmp; + int chip; + for (chip = 0; chip < ak->num_chips; chip++) { + tmp = snd_akm4xxx_get(ak, chip, addr); + /* clear the bits */ + tmp &= ~mask; + /* set the new bits */ + tmp |= value; + snd_akm4xxx_write(ak, chip, addr, tmp); + } +} + +/* + * change the rate of AK4620 + */ +static void qtet_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +{ + unsigned char ak4620_dfs; + + if (rate == 0) /* no hint - S/PDIF input is master or the new spdif + input rate undetected, simply return */ + return; + + /* adjust DFS on codecs - see datasheet */ + if (rate > 108000) + ak4620_dfs = AK4620_DFS1 | AK4620_CKS1; + else if (rate > 54000) + ak4620_dfs = AK4620_DFS0 | AK4620_CKS0; + else + ak4620_dfs = 0; + + /* set new value */ + qtet_akm_set_regs(ak, AK4620_DFS_REG, AK4620_DFS0 | AK4620_DFS1 | + AK4620_CKS0 | AK4620_CKS1, ak4620_dfs); +} + +#define AK_CONTROL(xname, xch) { .name = xname, .num_channels = xch } + +#define PCM_12_PLAYBACK_VOLUME "PCM 1/2 Playback Volume" +#define PCM_34_PLAYBACK_VOLUME "PCM 3/4 Playback Volume" +#define PCM_12_CAPTURE_VOLUME "PCM 1/2 Capture Volume" +#define PCM_34_CAPTURE_VOLUME "PCM 3/4 Capture Volume" + +static const struct snd_akm4xxx_dac_channel qtet_dac[] = { + AK_CONTROL(PCM_12_PLAYBACK_VOLUME, 2), + AK_CONTROL(PCM_34_PLAYBACK_VOLUME, 2), +}; + +static const struct snd_akm4xxx_adc_channel qtet_adc[] = { + AK_CONTROL(PCM_12_CAPTURE_VOLUME, 2), + AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2), +}; + +static struct snd_akm4xxx akm_qtet_dac __devinitdata = { + .type = SND_AK4620, + .num_dacs = 4, /* DAC1 - Output 12 + */ + .num_adcs = 4, /* ADC1 - Input 12 + */ + .ops = { + .write = qtet_akm_write, + .set_rate_val = qtet_akm_set_rate_val, + }, + .dac_info = qtet_dac, + .adc_info = qtet_adc, +}; + +/* Communication routines with the CPLD */ + + +/* Writes data to external register reg, both reg and data are + * GPIO representations */ +static void reg_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int tmp; + + mutex_lock(&ice->gpio_mutex); + /* set direction of used GPIOs*/ + /* all outputs */ + tmp = 0x00ffff; + ice->gpio.set_dir(ice, tmp); + /* mask - writable bits */ + ice->gpio.set_mask(ice, ~(tmp)); + /* write the data */ + tmp = ice->gpio.get_data(ice); + tmp &= ~GPIO_DATA_MASK; + tmp |= data; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop output enable */ + tmp &= ~GPIO_EX_GPIOE; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop the register gpio */ + tmp &= ~reg; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* raise the register GPIO */ + tmp |= reg; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* raise all data gpios */ + tmp |= GPIO_DATA_MASK; + ice->gpio.set_data(ice, tmp); + /* mask - immutable bits */ + ice->gpio.set_mask(ice, 0xffffff); + /* outputs only 8-15 */ + ice->gpio.set_dir(ice, 0x00ff00); + mutex_unlock(&ice->gpio_mutex); +} + +static unsigned int get_scr(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->scr; +} + +static unsigned int get_mcr(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->mcr; +} + +static unsigned int get_cpld(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->cpld; +} + +static void set_scr(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_SCR, val); + spec->scr = val; +} + +static void set_mcr(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_MCR, val); + spec->mcr = val; +} + +static void set_cpld(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_CPLD_CSN, val); + spec->cpld = val; +} +#ifdef CONFIG_PROC_FS +static void proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + char bin_buffer[36]; + + snd_iprintf(buffer, "SCR: %s\n", get_binary(bin_buffer, + get_scr(ice))); + snd_iprintf(buffer, "MCR: %s\n", get_binary(bin_buffer, + get_mcr(ice))); + snd_iprintf(buffer, "CPLD: %s\n", get_binary(bin_buffer, + get_cpld(ice))); +} + +static void proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "quartet", &entry)) + snd_info_set_text_ops(entry, ice, proc_regs_read); +} +#else /* !CONFIG_PROC_FS */ +static void proc_init(struct snd_ice1712 *ice) {} +#endif + +static int qtet_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + val = get_scr(ice) & SCR_MUTE; + ucontrol->value.integer.value[0] = (val) ? 0 : 1; + return 0; +} + +static int qtet_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new, smute; + old = get_scr(ice) & SCR_MUTE; + if (ucontrol->value.integer.value[0]) { + /* unmute */ + new = 0; + /* un-smuting DAC */ + smute = 0; + } else { + /* mute */ + new = SCR_MUTE; + /* smuting DAC */ + smute = AK4620_SMUTE; + } + if (old != new) { + struct snd_akm4xxx *ak = ice->akm; + set_scr(ice, (get_scr(ice) & ~SCR_MUTE) | new); + /* set smute */ + qtet_akm_set_regs(ak, AK4620_DEEMVOL_REG, AK4620_SMUTE, smute); + return 1; + } + /* no change */ + return 0; +} + +static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"}; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(texts); + + 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 qtet_ain12_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val, result; + val = get_scr(ice) & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + switch (val) { + case SCR_AIN12_LINE: + result = 0; + break; + case SCR_AIN12_MIC: + result = 1; + break; + case SCR_AIN12_LOWCUT: + result = 2; + break; + default: + /* BUG - no other combinations allowed */ + snd_BUG(); + result = 0; + } + ucontrol->value.integer.value[0] = result; + return 0; +} + +static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new, tmp, masked_old; + old = new = get_scr(ice); + masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + tmp = ucontrol->value.integer.value[0]; + if (tmp == 2) + tmp = 3; /* binary 10 is not supported */ + tmp <<= 4; /* shifting to SCR_AIN12_SEL0 */ + if (tmp != masked_old) { + /* change requested */ + switch (tmp) { + case SCR_AIN12_LINE: + new = old & ~(SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + set_scr(ice, new); + /* turn off relay */ + new &= ~SCR_RELAY; + set_scr(ice, new); + break; + case SCR_AIN12_MIC: + /* turn on relay */ + new = old | SCR_RELAY; + set_scr(ice, new); + new = (new & ~SCR_AIN12_SEL1) | SCR_AIN12_SEL0; + set_scr(ice, new); + break; + case SCR_AIN12_LOWCUT: + /* turn on relay */ + new = old | SCR_RELAY; + set_scr(ice, new); + new |= SCR_AIN12_SEL1 | SCR_AIN12_SEL0; + set_scr(ice, new); + break; + default: + snd_BUG(); + } + return 1; + } + /* no change */ + return 0; +} + +static int qtet_php_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + /* if phantom voltage =48V, phantom on */ + val = get_scr(ice) & SCR_PHP_V; + ucontrol->value.integer.value[0] = val ? 1 : 0; + return 0; +} + +static int qtet_php_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new; + old = new = get_scr(ice); + if (ucontrol->value.integer.value[0] /* phantom on requested */ + && (~old & SCR_PHP_V)) /* 0 = voltage 5V */ { + /* is off, turn on */ + /* turn voltage on first, = 1 */ + new = old | SCR_PHP_V; + set_scr(ice, new); + /* turn phantom on, = 0 */ + new &= ~SCR_PHP; + set_scr(ice, new); + } else if (!ucontrol->value.integer.value[0] && (old & SCR_PHP_V)) { + /* phantom off requested and 1 = voltage 48V */ + /* is on, turn off */ + /* turn voltage off first, = 0 */ + new = old & ~SCR_PHP_V; + set_scr(ice, new); + /* turn phantom off, = 1 */ + new |= SCR_PHP; + set_scr(ice, new); + } + if (old != new) + return 1; + /* no change */ + return 0; +} + +#define PRIV_SW(xid, xbit, xreg) [xid] = {.bit = xbit,\ + .set_register = set_##xreg,\ + .get_register = get_##xreg, } + + +#define PRIV_ENUM2(xid, xbit, xreg, xtext1, xtext2) [xid] = {.bit = xbit,\ + .set_register = set_##xreg,\ + .get_register = get_##xreg,\ + .texts = {xtext1, xtext2} } + +static struct qtet_kcontrol_private qtet_privates[] = { + PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"), + PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"), + PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"), + PRIV_ENUM2(COAX_OUT, CPLD_COAX_OUT, cpld, "IEC958", "I2S"), + PRIV_SW(IN12_MON12, MCR_IN12_MON12, mcr), + PRIV_SW(IN12_MON34, MCR_IN12_MON34, mcr), + PRIV_SW(IN34_MON12, MCR_IN34_MON12, mcr), + PRIV_SW(IN34_MON34, MCR_IN34_MON34, mcr), + PRIV_SW(OUT12_MON34, MCR_OUT12_MON34, mcr), + PRIV_SW(OUT34_MON12, MCR_OUT34_MON12, mcr), +}; + +static int qtet_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(private.texts); + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + private.texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int qtet_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + (private.get_register(ice) & private.bit) ? 1 : 0; + return 0; +} + +static int qtet_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new; + old = private.get_register(ice); + if (ucontrol->value.integer.value[0]) + new = old | private.bit; + else + new = old & ~private.bit; + if (old != new) { + private.set_register(ice, new); + return 1; + } + /* no change */ + return 0; +} + +#define qtet_sw_info snd_ctl_boolean_mono_info + +#define QTET_CONTROL(xname, xtype, xpriv) \ + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname,\ + .info = qtet_##xtype##_info,\ + .get = qtet_sw_get,\ + .put = qtet_sw_put,\ + .private_value = xpriv } + +static struct snd_kcontrol_new qtet_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = qtet_sw_info, + .get = qtet_mute_get, + .put = qtet_mute_put, + .private_value = 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Phantom Power", + .info = qtet_sw_info, + .get = qtet_php_get, + .put = qtet_php_put, + .private_value = 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog In 1/2 Capture Switch", + .info = qtet_ain12_enum_info, + .get = qtet_ain12_sw_get, + .put = qtet_ain12_sw_put, + .private_value = 0 + }, + QTET_CONTROL("Analog In 3/4 Capture Switch", enum, AIN34_SEL), + QTET_CONTROL("PCM In 1/2 Capture Switch", enum, IN12_SEL), + QTET_CONTROL("PCM In 3/4 Capture Switch", enum, IN34_SEL), + QTET_CONTROL("Coax Output Source", enum, COAX_OUT), + QTET_CONTROL("Analog In 1/2 to Monitor 1/2", sw, IN12_MON12), + QTET_CONTROL("Analog In 1/2 to Monitor 3/4", sw, IN12_MON34), + QTET_CONTROL("Analog In 3/4 to Monitor 1/2", sw, IN34_MON12), + QTET_CONTROL("Analog In 3/4 to Monitor 3/4", sw, IN34_MON34), + QTET_CONTROL("Output 1/2 to Monitor 3/4", sw, OUT12_MON34), + QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12), +}; + +static char *slave_vols[] __devinitdata = { + PCM_12_PLAYBACK_VOLUME, + PCM_34_PLAYBACK_VOLUME, + NULL +}; + +static __devinitdata +DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1); + +static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, + const char *name) +{ + struct snd_ctl_elem_id sid; + memset(&sid, 0, sizeof(sid)); + /* FIXME: strcpy is bad. */ + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_find_id(card, &sid); +} + +static void __devinit add_slaves(struct snd_card *card, + struct snd_kcontrol *master, char **list) +{ + for (; *list; list++) { + struct snd_kcontrol *slave = ctl_find(card, *list); + if (slave) + snd_ctl_add_slave(master, slave); + } +} + +static int __devinit qtet_add_controls(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + int err, i; + struct snd_kcontrol *vmaster; + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + for (i = 0; i < ARRAY_SIZE(qtet_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&qtet_controls[i], ice)); + if (err < 0) + return err; + } + + /* Create virtual master control */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + qtet_master_db_scale); + if (!vmaster) + return -ENOMEM; + add_slaves(ice->card, vmaster, slave_vols); + err = snd_ctl_add(ice->card, vmaster); + if (err < 0) + return err; + /* only capture SPDIF over AK4113 */ + err = snd_ak4113_build(spec->ak4113, + ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + if (err < 0) + return err; + return 0; +} + +static inline int qtet_is_spdif_master(struct snd_ice1712 *ice) +{ + /* CPLD_SYNC_SEL: 0 = internal, 1 = external (i.e. spdif master) */ + return (get_cpld(ice) & CPLD_SYNC_SEL) ? 1 : 0; +} + +static unsigned int qtet_get_rate(struct snd_ice1712 *ice) +{ + int i; + unsigned char result; + + result = get_cpld(ice) & CPLD_CKS_MASK; + for (i = 0; i < ARRAY_SIZE(cks_vals); i++) + if (cks_vals[i] == result) + return qtet_rates[i]; + return 0; +} + +static int get_cks_val(int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(qtet_rates); i++) + if (qtet_rates[i] == rate) + return cks_vals[i]; + return 0; +} + +/* setting new rate */ +static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned int new; + unsigned char val; + /* switching ice1724 to external clock - supplied by ext. circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + + new = (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate); + /* switch to internal clock, drop CPLD_SYNC_SEL */ + new &= ~CPLD_SYNC_SEL; + /* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n", + get_cpld(ice), new); */ + set_cpld(ice, new); +} + +static inline unsigned char qtet_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* no change in master clock */ + return 0; +} + +/* setting clock to external - SPDIF */ +static int qtet_set_spdif_clock(struct snd_ice1712 *ice, int type) +{ + unsigned int old, new; + + old = new = get_cpld(ice); + new &= ~(CPLD_CKS_MASK | CPLD_WORD_SEL); + switch (type) { + case EXT_SPDIF_TYPE: + new |= CPLD_EXT_SPDIF; + break; + case EXT_WORDCLOCK_1FS_TYPE: + new |= CPLD_EXT_WORDCLOCK_1FS; + break; + case EXT_WORDCLOCK_256FS_TYPE: + new |= CPLD_EXT_WORDCLOCK_256FS; + break; + default: + snd_BUG(); + } + if (old != new) { + set_cpld(ice, new); + /* changed */ + return 1; + } + return 0; +} + +static int qtet_get_spdif_master_type(struct snd_ice1712 *ice) +{ + unsigned int val; + int result; + val = get_cpld(ice); + /* checking only rate/clock-related bits */ + val &= (CPLD_CKS_MASK | CPLD_WORD_SEL | CPLD_SYNC_SEL); + if (!(val & CPLD_SYNC_SEL)) { + /* switched to internal clock, is not any external type */ + result = -1; + } else { + switch (val) { + case (CPLD_EXT_SPDIF): + result = EXT_SPDIF_TYPE; + break; + case (CPLD_EXT_WORDCLOCK_1FS): + result = EXT_WORDCLOCK_1FS_TYPE; + break; + case (CPLD_EXT_WORDCLOCK_256FS): + result = EXT_WORDCLOCK_256FS_TYPE; + break; + default: + /* undefined combination of external clock setup */ + snd_BUG(); + result = 0; + } + } + return result; +} + +/* Called when ak4113 detects change in the input SPDIF stream */ +static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0, + unsigned char c1) +{ + struct snd_ice1712 *ice = ak4113->change_callback_private; + int rate; + if ((qtet_get_spdif_master_type(ice) == EXT_SPDIF_TYPE) && + c1) { + /* only for SPDIF master mode, rate was changed */ + rate = snd_ak4113_external_rate(ak4113); + /* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n", + rate); */ + qtet_akm_set_rate_val(ice->akm, rate); + } +} + +/* + * If clock slaved to SPDIF-IN, setting runtime rate + * to the detected external rate + */ +static void qtet_spdif_in_open(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct qtet_spec *spec = ice->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int rate; + + if (qtet_get_spdif_master_type(ice) != EXT_SPDIF_TYPE) + /* not external SPDIF, no rate limitation */ + return; + /* only external SPDIF can detect incoming sample rate */ + rate = snd_ak4113_external_rate(spec->ak4113); + if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } +} + +/* + * initialize the chip + */ +static int __devinit qtet_init(struct snd_ice1712 *ice) +{ + static const unsigned char ak4113_init_vals[] = { + /* AK4113_REG_PWRDN */ AK4113_RST | AK4113_PWN | + AK4113_OCKS0 | AK4113_OCKS1, + /* AK4113_REQ_FORMAT */ AK4113_DIF_I24I2S | AK4113_VTX | + AK4113_DEM_OFF | AK4113_DEAU, + /* AK4113_REG_IO0 */ AK4113_OPS2 | AK4113_TXE | + AK4113_XTL_24_576M, + /* AK4113_REG_IO1 */ AK4113_EFH_1024LRCLK | AK4113_IPS(0), + /* AK4113_REG_INT0_MASK */ 0, + /* AK4113_REG_INT1_MASK */ 0, + /* AK4113_REG_DATDTS */ 0, + }; + int err; + struct qtet_spec *spec; + struct snd_akm4xxx *ak; + unsigned char val; + + /* switching ice1724 to external clock - supplied by ext. circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + /* qtet is clocked by Xilinx array */ + ice->hw_rates = &qtet_rates_info; + ice->is_spdif_master = qtet_is_spdif_master; + ice->get_rate = qtet_get_rate; + ice->set_rate = qtet_set_rate; + ice->set_mclk = qtet_set_mclk; + ice->set_spdif_clock = qtet_set_spdif_clock; + ice->get_spdif_master_type = qtet_get_spdif_master_type; + ice->ext_clock_names = ext_clock_names; + ice->ext_clock_count = ARRAY_SIZE(ext_clock_names); + /* since Qtet can detect correct SPDIF-in rate, all streams can be + * limited to this specific rate */ + ice->spdif.ops.open = ice->pro_open = qtet_spdif_in_open; + ice->spec = spec; + + /* Mute Off */ + /* SCR Initialize*/ + /* keep codec power down first */ + set_scr(ice, SCR_PHP); + udelay(1); + /* codec power up */ + set_scr(ice, SCR_PHP | SCR_CODEC_PDN); + + /* MCR Initialize */ + set_mcr(ice, 0); + + /* CPLD Initialize */ + set_cpld(ice, 0); + + + ice->num_total_dacs = 2; + ice->num_total_adcs = 2; + + ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL); + ak = ice->akm; + if (!ak) + return -ENOMEM; + /* only one codec with two chips */ + ice->akm_codecs = 1; + err = snd_ice1712_akm4xxx_init(ak, &akm_qtet_dac, NULL, ice); + if (err < 0) + return err; + err = snd_ak4113_create(ice->card, + qtet_ak4113_read, + qtet_ak4113_write, + ak4113_init_vals, + ice, &spec->ak4113); + if (err < 0) + return err; + /* callback for codecs rate setting */ + spec->ak4113->change_callback = qtet_ak4113_change; + spec->ak4113->change_callback_private = ice; + /* AK41143 in Quartet can detect external rate correctly + * (i.e. check_flags = 0) */ + spec->ak4113->check_flags = 0; + + proc_init(ice); + + qtet_set_rate(ice, 44100); + return 0; +} + +static unsigned char qtet_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC, + 1xDACs, SPDIF in */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, in, out-ext */ + [ICE_EEP2_GPIO_DIR] = 0x00, /* 0-7 inputs, switched to output + only during output operations */ + [ICE_EEP2_GPIO_DIR1] = 0xff, /* 8-15 outputs */ + [ICE_EEP2_GPIO_DIR2] = 0x00, + [ICE_EEP2_GPIO_MASK] = 0xff, /* changed only for OUT operations */ + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0xff, + + [ICE_EEP2_GPIO_STATE] = 0x00, /* inputs */ + [ICE_EEP2_GPIO_STATE1] = 0x7d, /* all 1, but GPIO_CPLD_RW + and GPIO15 always zero */ + [ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */ +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_QTET, + .name = "Infrasonic Quartet", + .model = "quartet", + .chip_init = qtet_init, + .build_controls = qtet_add_controls, + .eeprom_size = sizeof(qtet_eeprom), + .eeprom_data = qtet_eeprom, + }, + { } /* terminator */ +}; diff --git a/sound/pci/ice1712/quartet.h b/sound/pci/ice1712/quartet.h new file mode 100644 index 0000000..80809b7 --- /dev/null +++ b/sound/pci/ice1712/quartet.h @@ -0,0 +1,10 @@ +#ifndef __SOUND_QTET_H +#define __SOUND_QTET_H + +#define QTET_DEVICE_DESC "{Infrasonic,Quartet}," + +#define VT1724_SUBDEVICE_QTET 0x30305349 /* Infrasonic Quartet */ + +extern struct snd_ice1712_card_info snd_vt1724_qtet_cards[]; + +#endif /* __SOUND_QTET_H */ -- cgit v0.10.2 From 87b61902ce3dec23a2d8256b9cfcf4e28786a320 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:05:18 +0200 Subject: sound: oxygen: do not try to restore nonexistent EEPROM On cards where the EEPROM was deliberately omitted, we do not need to try to restore the EEPROM's contents. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 9a8936e..c9f2714 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -278,7 +278,11 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[]) static void oxygen_restore_eeprom(struct oxygen *chip, const struct pci_device_id *id) { - if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) { + u16 eeprom_id; + + eeprom_id = oxygen_read_eeprom(chip, 0); + if (eeprom_id != OXYGEN_EEPROM_ID && + (eeprom_id != 0xffff || id->subdevice != 0x8788)) { /* * This function gets called only when a known card model has * been detected, i.e., we know there is a valid subsystem -- cgit v0.10.2 From 362bc24d6746bcd49bb4853fc5aa7d4c728b3f9e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:05:58 +0200 Subject: sound: oxygen: fix for PI7C9X110 compatibility If the card is used with a Pericom PI7C9X110 PCI-E/PCI bridge, reconfigure the latter's PCI buffering to fix an unknown problem. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index c9f2714..9c5e645 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -307,6 +307,28 @@ static void oxygen_restore_eeprom(struct oxygen *chip, } } +static void pci_bridge_magic(void) +{ + struct pci_dev *pci = NULL; + u32 tmp; + + for (;;) { + /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */ + pci = pci_get_device(0x12d8, 0xe110, pci); + if (!pci) + break; + /* + * ... configure its secondary internal arbiter to park to + * the secondary port, instead of to the last master. + */ + if (!pci_read_config_dword(pci, 0x40, &tmp)) { + tmp |= 1; + pci_write_config_dword(pci, 0x40, tmp); + } + /* Why? Try asking C-Media. */ + } +} + static void oxygen_init(struct oxygen *chip) { unsigned int i; @@ -585,6 +607,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, snd_card_set_dev(card, &pci->dev); card->private_free = oxygen_card_free; + pci_bridge_magic(); oxygen_init(chip); chip->model.init(chip); -- cgit v0.10.2 From 65c3ac885ce9852852b895a4a62212f62cb5f2e9 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:11:27 +0200 Subject: sound: virtuoso: split virtuoso.c The virtuoso.c file has become rather big. This patch splits it up so that only code for very similar card models is in one file. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 4ba07d4..389941c 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,7 +1,8 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o snd-hifier-objs := hifier.o snd-oxygen-objs := oxygen.o -snd-virtuoso-objs := virtuoso.o +snd-virtuoso-objs := virtuoso.o xonar_lib.o \ + xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o obj-$(CONFIG_SND_HIFIER) += snd-hifier.o diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 6ebcb6b..6accaf9 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -17,145 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * Xonar D2/D2X - * ------------ - * - * CMI8788: - * - * SPI 0 -> 1st PCM1796 (front) - * SPI 1 -> 2nd PCM1796 (surround) - * SPI 2 -> 3rd PCM1796 (center/LFE) - * SPI 4 -> 4th PCM1796 (back) - * - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 5 <- external power present (D2X only) - * GPIO 7 -> ALT - * GPIO 8 -> enable output to speakers - */ - -/* - * Xonar D1/DX - * ----------- - * - * CMI8788: - * - * I²C <-> CS4398 (front) - * <-> CS4362A (surround, center/LFE, back) - * - * GPI 0 <- external power present (DX only) - * - * GPIO 0 -> enable output to speakers - * GPIO 1 -> enable front panel I/O - * GPIO 2 -> M0 of CS5361 - * GPIO 3 -> M1 of CS5361 - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) - * - * CS4398: - * - * AD0 <- 1 - * AD1 <- 1 - * - * CS4362A: - * - * AD0 <- 0 - */ - -/* - * Xonar HDAV1.3 (Deluxe) - * ---------------------- - * - * CMI8788: - * - * I²C <-> PCM1796 (front) - * - * GPI 0 <- external power present - * - * GPIO 0 -> enable output to speakers - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) - * - * TXD -> HDMI controller - * RXD <- HDMI controller - * - * PCM1796 front: AD1,0 <- 0,0 - * - * no daughterboard - * ---------------- - * - * GPIO 4 <- 1 - * - * H6 daughterboard - * ---------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 0 - * - * I²C <-> PCM1796 (surround) - * <-> PCM1796 (center/LFE) - * <-> PCM1796 (back) - * - * PCM1796 surround: AD1,0 <- 0,1 - * PCM1796 center/LFE: AD1,0 <- 1,0 - * PCM1796 back: AD1,0 <- 1,1 - * - * unknown daughterboard - * --------------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 1 - * - * I²C <-> CS4362A (surround, center/LFE, back) - * - * CS4362A: AD0 <- 0 - */ - -/* - * Xonar Essence ST (Deluxe)/STX - * ----------------------------- - * - * CMI8788: - * - * I²C <-> PCM1792A - * - * GPI 0 <- external power present - * - * GPIO 0 -> enable output to speakers - * GPIO 1 -> route HP to front panel (0) or rear jack (1) - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 7 -> route output to speaker jacks (0) or HP (1) - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) - * - * PCM1792A: - * - * AD0 <- 0 - * - * H6 daughterboard - * ---------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 0 - */ - #include #include -#include -#include -#include -#include #include #include #include -#include -#include -#include "oxygen.h" -#include "cm9780.h" -#include "pcm1796.h" -#include "cs4398.h" -#include "cs4362a.h" +#include "xonar.h" MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("Asus AVx00 driver"); @@ -173,972 +40,28 @@ MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable card"); -enum { - MODEL_D2, - MODEL_D2X, - MODEL_D1, - MODEL_DX, - MODEL_HDAV, /* without daughterboard */ - MODEL_HDAV_H6, /* with H6 daughterboard */ - MODEL_ST, - MODEL_ST_H6, - MODEL_STX, -}; - static struct pci_device_id xonar_ids[] __devinitdata = { - { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 }, - { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, - { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, - { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV }, - { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX }, - { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, - { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX }, - { OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST }, + { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8275) }, + { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8314) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8327) }, + { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } }; MODULE_DEVICE_TABLE(pci, xonar_ids); - -#define GPIO_CS53x1_M_MASK 0x000c -#define GPIO_CS53x1_M_SINGLE 0x0000 -#define GPIO_CS53x1_M_DOUBLE 0x0004 -#define GPIO_CS53x1_M_QUAD 0x0008 - -#define GPIO_D2X_EXT_POWER 0x0020 -#define GPIO_D2_ALT 0x0080 -#define GPIO_D2_OUTPUT_ENABLE 0x0100 - -#define GPI_DX_EXT_POWER 0x01 -#define GPIO_DX_OUTPUT_ENABLE 0x0001 -#define GPIO_DX_FRONT_PANEL 0x0002 -#define GPIO_DX_INPUT_ROUTE 0x0100 - -#define GPIO_DB_MASK 0x0030 -#define GPIO_DB_H6 0x0000 -#define GPIO_DB_XX 0x0020 - -#define GPIO_ST_HP_REAR 0x0002 -#define GPIO_ST_HP 0x0080 - -#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */ -#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ -#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ - -struct xonar_data { - unsigned int anti_pop_delay; - unsigned int dacs; - u16 output_enable_bit; - u8 ext_power_reg; - u8 ext_power_int_reg; - u8 ext_power_bit; - u8 has_power; - u8 pcm1796_oversampling; - u8 cs4398_fm; - u8 cs4362a_fm; - u8 hdmi_params[5]; -}; - -static void xonar_gpio_changed(struct oxygen *chip); - -static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - /* maps ALSA channel pair number to SPI output */ - static const u8 codec_map[4] = { - 0, 1, 2, 4 - }; - oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | - OXYGEN_SPI_DATA_LENGTH_2 | - OXYGEN_SPI_CLOCK_160 | - (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | - OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - (reg << 8) | value); -} - -static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value); -} - -static void pcm1796_write(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == - OXYGEN_FUNCTION_SPI) - pcm1796_write_spi(chip, codec, reg, value); - else - pcm1796_write_i2c(chip, codec, reg, value); -} - -static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); -} - -static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); -} - -static void hdmi_write_command(struct oxygen *chip, u8 command, - unsigned int count, const u8 *params) -{ - unsigned int i; - u8 checksum; - - oxygen_write_uart(chip, 0xfb); - oxygen_write_uart(chip, 0xef); - oxygen_write_uart(chip, command); - oxygen_write_uart(chip, count); - for (i = 0; i < count; ++i) - oxygen_write_uart(chip, params[i]); - checksum = 0xfb + 0xef + command + count; - for (i = 0; i < count; ++i) - checksum += params[i]; - oxygen_write_uart(chip, checksum); -} - -static void xonar_enable_output(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - msleep(data->anti_pop_delay); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); -} - -static void xonar_common_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - if (data->ext_power_reg) { - oxygen_set_bits8(chip, data->ext_power_int_reg, - data->ext_power_bit); - chip->interrupt_mask |= OXYGEN_INT_GPIO; - chip->model.gpio_changed = xonar_gpio_changed; - data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) - & data->ext_power_bit); - } - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_CS53x1_M_MASK | data->output_enable_bit); - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); - oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); - xonar_enable_output(chip); -} - -static void update_pcm1796_volume(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); - } -} - -static void update_pcm1796_mute(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - u8 value; - - value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; - if (chip->dac_mute) - value |= PCM1796_MUTE; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 18, value); -} - -static void pcm1796_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); - pcm1796_write(chip, i, 20, data->pcm1796_oversampling); - pcm1796_write(chip, i, 21, 0); - } - update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ - update_pcm1796_volume(chip); -} - -static void xonar_d2_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->anti_pop_delay = 300; - data->dacs = 4; - data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); - - xonar_common_init(chip); - - snd_component_add(chip->card, "PCM1796"); - snd_component_add(chip->card, "CS5381"); -} - -static void xonar_d2x_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->ext_power_reg = OXYGEN_GPIO_DATA; - data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; - data->ext_power_bit = GPIO_D2X_EXT_POWER; - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); - - xonar_d2_init(chip); -} - -static void update_cs4362a_volumes(struct oxygen *chip) -{ - u8 mute; - - mute = chip->dac_mute ? CS4362A_MUTE : 0; - cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); - cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); - cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); - cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); - cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); - cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); -} - -static void update_cs43xx_volume(struct oxygen *chip) -{ - cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); - cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); - update_cs4362a_volumes(chip); -} - -static void update_cs43xx_mute(struct oxygen *chip) -{ - u8 reg; - - reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; - if (chip->dac_mute) - reg |= CS4398_MUTE_B | CS4398_MUTE_A; - cs4398_write(chip, 4, reg); - update_cs4362a_volumes(chip); -} - -static void cs43xx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - /* set CPEN (control port mode) and power down */ - cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); - cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); - /* configure */ - cs4398_write(chip, 2, data->cs4398_fm); - cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); - cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | - CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); - cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); - cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | - CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); - cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); - cs4362a_write(chip, 0x05, 0); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); - update_cs43xx_volume(chip); - update_cs43xx_mute(chip); - /* clear power down */ - cs4398_write(chip, 8, CS4398_CPEN); - cs4362a_write(chip, 0x01, CS4362A_CPEN); -} - -static void xonar_d1_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->anti_pop_delay = 800; - data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; - data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_FM_SINGLE | - CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - cs43xx_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, - GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); - - xonar_common_init(chip); - - snd_component_add(chip->card, "CS4398"); - snd_component_add(chip->card, "CS4362A"); - snd_component_add(chip->card, "CS5361"); -} - -static void xonar_dx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; - - xonar_d1_init(chip); -} - -static void xonar_hdav_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 param; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - data->anti_pop_delay = 100; - data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1; - data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE); - - oxygen_reset_uart(chip); - param = 0; - hdmi_write_command(chip, 0x61, 1, ¶m); - param = 1; - hdmi_write_command(chip, 0x74, 1, ¶m); - data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; - data->hdmi_params[4] = 1; - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); - - xonar_common_init(chip); - - snd_component_add(chip->card, "PCM1796"); - snd_component_add(chip->card, "CS5381"); -} - -static void xonar_st_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - if (chip->model.private_data == MODEL_ST_H6) - chip->model.dac_channels = 8; - data->anti_pop_delay = 100; - data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1; - data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, - GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); - - xonar_common_init(chip); - - snd_component_add(chip->card, "PCM1792A"); - snd_component_add(chip->card, "CS5381"); -} - -static void xonar_stx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; - - xonar_st_init(chip); -} - -static void xonar_disable_output(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); -} - -static void xonar_d2_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); -} - -static void xonar_d1_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); - cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); - oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); -} - -static void xonar_hdav_cleanup(struct oxygen *chip) -{ - u8 param = 0; - - hdmi_write_command(chip, 0x74, 1, ¶m); - xonar_disable_output(chip); -} - -static void xonar_st_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); -} - -static void xonar_d2_suspend(struct oxygen *chip) -{ - xonar_d2_cleanup(chip); -} - -static void xonar_d1_suspend(struct oxygen *chip) -{ - xonar_d1_cleanup(chip); -} - -static void xonar_hdav_suspend(struct oxygen *chip) -{ - xonar_hdav_cleanup(chip); - msleep(2); -} - -static void xonar_st_suspend(struct oxygen *chip) -{ - xonar_st_cleanup(chip); -} - -static void xonar_d2_resume(struct oxygen *chip) -{ - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_d1_resume(struct oxygen *chip) -{ - oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); - msleep(1); - cs43xx_init(chip); - xonar_enable_output(chip); -} - -static void xonar_hdav_resume(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 param; - - oxygen_reset_uart(chip); - param = 0; - hdmi_write_command(chip, 0x61, 1, ¶m); - param = 1; - hdmi_write_command(chip, 0x74, 1, ¶m); - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_st_resume(struct oxygen *chip) -{ - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_hdav_pcm_hardware_filter(unsigned int channel, - struct snd_pcm_hardware *hardware) -{ - if (channel == PCM_MULTICH) { - hardware->rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000; - hardware->rate_min = 44100; - } -} - -static void set_pcm1796_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - data->pcm1796_oversampling = - params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 20, data->pcm1796_oversampling); -} - -static void set_cs53x1_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - unsigned int value; - - if (params_rate(params) <= 54000) - value = GPIO_CS53x1_M_SINGLE; - else if (params_rate(params) <= 108000) - value = GPIO_CS53x1_M_DOUBLE; - else - value = GPIO_CS53x1_M_QUAD; - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - value, GPIO_CS53x1_M_MASK); -} - -static void set_cs43xx_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - if (params_rate(params) <= 50000) { - data->cs4398_fm |= CS4398_FM_SINGLE; - data->cs4362a_fm |= CS4362A_FM_SINGLE; - } else if (params_rate(params) <= 100000) { - data->cs4398_fm |= CS4398_FM_DOUBLE; - data->cs4362a_fm |= CS4362A_FM_DOUBLE; - } else { - data->cs4398_fm |= CS4398_FM_QUAD; - data->cs4362a_fm |= CS4362A_FM_QUAD; - } - cs4398_write(chip, 2, data->cs4398_fm); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); -} - -static void set_hdmi_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - - data->hdmi_params[0] = 0; /* 1 = non-audio */ - switch (params_rate(params)) { - case 44100: - data->hdmi_params[1] = IEC958_AES3_CON_FS_44100; - break; - case 48000: - data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; - break; - default: /* 96000 */ - data->hdmi_params[1] = IEC958_AES3_CON_FS_96000; - break; - case 192000: - data->hdmi_params[1] = IEC958_AES3_CON_FS_192000; - break; - } - data->hdmi_params[2] = params_channels(params) / 2 - 1; - if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) - data->hdmi_params[3] = 0; - else - data->hdmi_params[3] = 0xc0; - data->hdmi_params[4] = 1; /* ? */ - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); -} - -static void set_hdav_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - set_pcm1796_params(chip, params); - set_hdmi_params(chip, params); -} - -static void xonar_gpio_changed(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 has_power; - - has_power = !!(oxygen_read8(chip, data->ext_power_reg) - & data->ext_power_bit); - if (has_power != data->has_power) { - data->has_power = has_power; - if (has_power) { - snd_printk(KERN_NOTICE "power restored\n"); - } else { - snd_printk(KERN_CRIT - "Hey! Don't unplug the power cable!\n"); - /* TODO: stop PCMs */ - } - } -} - -static void xonar_hdav_uart_input(struct oxygen *chip) -{ - if (chip->uart_input_count >= 2 && - chip->uart_input[chip->uart_input_count - 2] == 'O' && - chip->uart_input[chip->uart_input_count - 1] == 'K') { - printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:\n"); - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, - chip->uart_input, chip->uart_input_count); - chip->uart_input_count = 0; - } -} - -static int gpio_bit_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 bit = ctl->private_value; - - value->value.integer.value[0] = - !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); - return 0; -} - -static int gpio_bit_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 bit = ctl->private_value; - u16 old_bits, new_bits; - int changed; - - spin_lock_irq(&chip->reg_lock); - old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); - if (value->value.integer.value[0]) - new_bits = old_bits | bit; - else - new_bits = old_bits & ~bit; - changed = new_bits != old_bits; - if (changed) - oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); - spin_unlock_irq(&chip->reg_lock); - return changed; -} - -static const struct snd_kcontrol_new alt_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Loopback Switch", - .info = snd_ctl_boolean_mono_info, - .get = gpio_bit_switch_get, - .put = gpio_bit_switch_put, - .private_value = GPIO_D2_ALT, -}; - -static const struct snd_kcontrol_new front_panel_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Front Panel Switch", - .info = snd_ctl_boolean_mono_info, - .get = gpio_bit_switch_get, - .put = gpio_bit_switch_put, - .private_value = GPIO_DX_FRONT_PANEL, -}; - -static int st_output_switch_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" - }; - - info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - info->count = 1; - info->value.enumerated.items = 3; - if (info->value.enumerated.item >= 3) - info->value.enumerated.item = 2; - strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); - return 0; -} - -static int st_output_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 gpio; - - gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); - if (!(gpio & GPIO_ST_HP)) - value->value.enumerated.item[0] = 0; - else if (gpio & GPIO_ST_HP_REAR) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; - return 0; -} - - -static int st_output_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 gpio_old, gpio; - - mutex_lock(&chip->mutex); - gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); - gpio = gpio_old; - switch (value->value.enumerated.item[0]) { - case 0: - gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR); - break; - case 1: - gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR; - break; - case 2: - gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR; - break; - } - oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); - mutex_unlock(&chip->mutex); - return gpio != gpio_old; -} - -static const struct snd_kcontrol_new st_output_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output", - .info = st_output_switch_info, - .get = st_output_switch_get, - .put = st_output_switch_put, -}; - -static void xonar_line_mic_ac97_switch(struct oxygen *chip, - unsigned int reg, unsigned int mute) -{ - if (reg == AC97_LINE) { - spin_lock_irq(&chip->reg_lock); - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - mute ? GPIO_DX_INPUT_ROUTE : 0, - GPIO_DX_INPUT_ROUTE); - spin_unlock_irq(&chip->reg_lock); - } -} - -static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0); -static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); - -static int xonar_d2_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - /* CD in is actually connected to the video in pin */ - template->private_value ^= AC97_CD ^ AC97_VIDEO; - return 0; -} - -static int xonar_d1_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - -static int xonar_st_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - if (!strcmp(template->name, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ - return 0; -} - -static int xonar_d2_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); -} - -static int xonar_d1_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); -} - -static int xonar_st_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); -} - -static const struct oxygen_model model_xonar_d2 = { - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .init = xonar_d2_init, - .control_filter = xonar_d2_control_filter, - .mixer_init = xonar_d2_mixer_init, - .cleanup = xonar_d2_cleanup, - .suspend = xonar_d2_suspend, - .resume = xonar_d2_resume, - .set_dac_params = set_pcm1796_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .dac_tlv = pcm1796_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF | - MIDI_OUTPUT | - MIDI_INPUT, - .dac_channels = 8, - .dac_volume_min = 255 - 2*60, - .dac_volume_max = 255, - .misc_flags = OXYGEN_MISC_MIDI, - .function_flags = OXYGEN_FUNCTION_SPI | - OXYGEN_FUNCTION_ENABLE_SPI_4_5, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - -static const struct oxygen_model model_xonar_d1 = { - .longname = "Asus Virtuoso 100", - .chip = "AV200", - .init = xonar_d1_init, - .control_filter = xonar_d1_control_filter, - .mixer_init = xonar_d1_mixer_init, - .cleanup = xonar_d1_cleanup, - .suspend = xonar_d1_suspend, - .resume = xonar_d1_resume, - .set_dac_params = set_cs43xx_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_cs43xx_volume, - .update_dac_mute = update_cs43xx_mute, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = cs4362a_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2, - .dac_channels = 8, - .dac_volume_min = 127 - 60, - .dac_volume_max = 127, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - -static const struct oxygen_model model_xonar_hdav = { - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .init = xonar_hdav_init, - .cleanup = xonar_hdav_cleanup, - .suspend = xonar_hdav_suspend, - .resume = xonar_hdav_resume, - .pcm_hardware_filter = xonar_hdav_pcm_hardware_filter, - .set_dac_params = set_hdav_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .uart_input = xonar_hdav_uart_input, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = pcm1796_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF, - .dac_channels = 8, - .dac_volume_min = 255 - 2*60, - .dac_volume_max = 255, - .misc_flags = OXYGEN_MISC_MIDI, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - -static const struct oxygen_model model_xonar_st = { - .longname = "Asus Virtuoso 100", - .chip = "AV200", - .init = xonar_st_init, - .control_filter = xonar_st_control_filter, - .mixer_init = xonar_st_mixer_init, - .cleanup = xonar_st_cleanup, - .suspend = xonar_st_suspend, - .resume = xonar_st_resume, - .set_dac_params = set_pcm1796_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = pcm1796_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2, - .dac_channels = 2, - .dac_volume_min = 255 - 2*60, - .dac_volume_max = 255, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - static int __devinit get_xonar_model(struct oxygen *chip, const struct pci_device_id *id) { - static const struct oxygen_model *const models[] = { - [MODEL_D1] = &model_xonar_d1, - [MODEL_DX] = &model_xonar_d1, - [MODEL_D2] = &model_xonar_d2, - [MODEL_D2X] = &model_xonar_d2, - [MODEL_HDAV] = &model_xonar_hdav, - [MODEL_ST] = &model_xonar_st, - [MODEL_STX] = &model_xonar_st, - }; - static const char *const names[] = { - [MODEL_D1] = "Xonar D1", - [MODEL_DX] = "Xonar DX", - [MODEL_D2] = "Xonar D2", - [MODEL_D2X] = "Xonar D2X", - [MODEL_HDAV] = "Xonar HDAV1.3", - [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6", - [MODEL_ST] = "Xonar Essence ST", - [MODEL_ST_H6] = "Xonar Essence ST+H6", - [MODEL_STX] = "Xonar Essence STX", - }; - unsigned int model = id->driver_data; - - if (model >= ARRAY_SIZE(models) || !models[model]) - return -EINVAL; - chip->model = *models[model]; - - switch (model) { - case MODEL_D2X: - chip->model.init = xonar_d2x_init; - break; - case MODEL_DX: - chip->model.init = xonar_dx_init; - break; - case MODEL_HDAV: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { - case GPIO_DB_H6: - model = MODEL_HDAV_H6; - break; - case GPIO_DB_XX: - snd_printk(KERN_ERR "unknown daughterboard\n"); - return -ENODEV; - } - break; - case MODEL_ST: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { - case GPIO_DB_H6: - model = MODEL_ST_H6; - break; - } - break; - case MODEL_STX: - chip->model.init = xonar_stx_init; - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - break; - } - - chip->model.shortname = names[model]; - chip->model.private_data = model; - return 0; + if (get_xonar_pcm179x_model(chip, id) >= 0) + return 0; + if (get_xonar_cs43xx_model(chip, id) >= 0) + return 0; + return -EINVAL; } static int __devinit xonar_probe(struct pci_dev *pci, diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h new file mode 100644 index 0000000..89b3ed8 --- /dev/null +++ b/sound/pci/oxygen/xonar.h @@ -0,0 +1,50 @@ +#ifndef XONAR_H_INCLUDED +#define XONAR_H_INCLUDED + +#include "oxygen.h" + +struct xonar_generic { + unsigned int anti_pop_delay; + u16 output_enable_bit; + u8 ext_power_reg; + u8 ext_power_int_reg; + u8 ext_power_bit; + u8 has_power; +}; + +struct xonar_hdmi { + u8 params[5]; +}; + +/* generic helper functions */ + +void xonar_enable_output(struct oxygen *chip); +void xonar_disable_output(struct oxygen *chip); +void xonar_init_ext_power(struct oxygen *chip); +void xonar_init_cs53x1(struct oxygen *chip); +void xonar_set_cs53x1_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value); +int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value); + +/* model-specific card drivers */ + +int get_xonar_pcm179x_model(struct oxygen *chip, + const struct pci_device_id *id); +int get_xonar_cs43xx_model(struct oxygen *chip, + const struct pci_device_id *id); + +/* HDMI helper functions */ + +void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data); +void xonar_hdmi_cleanup(struct oxygen *chip); +void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi); +void xonar_hdmi_pcm_hardware_filter(unsigned int channel, + struct snd_pcm_hardware *hardware); +void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi, + struct snd_pcm_hw_params *params); +void xonar_hdmi_uart_input(struct oxygen *chip); + +#endif diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c new file mode 100644 index 0000000..8fb5797 --- /dev/null +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -0,0 +1,304 @@ +/* + * card driver for models with CS4398/CS4362A DACs (Xonar D1/DX) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * 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 driver; if not, see . + */ + +/* + * Xonar D1/DX + * ----------- + * + * CMI8788: + * + * I²C <-> CS4398 (front) + * <-> CS4362A (surround, center/LFE, back) + * + * GPI 0 <- external power present (DX only) + * + * GPIO 0 -> enable output to speakers + * GPIO 1 -> enable front panel I/O + * GPIO 2 -> M0 of CS5361 + * GPIO 3 -> M1 of CS5361 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * CS4398: + * + * AD0 <- 1 + * AD1 <- 1 + * + * CS4362A: + * + * AD0 <- 0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" +#include "cs4398.h" +#include "cs4362a.h" + +#define GPI_EXT_POWER 0x01 +#define GPIO_D1_OUTPUT_ENABLE 0x0001 +#define GPIO_D1_FRONT_PANEL 0x0002 +#define GPIO_D1_INPUT_ROUTE 0x0100 + +#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ +#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ + +struct xonar_cs43xx { + struct xonar_generic generic; + u8 cs4398_fm; + u8 cs4362a_fm; +}; + +static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); +} + +static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); +} + +static void update_cs4362a_volumes(struct oxygen *chip) +{ + u8 mute; + + mute = chip->dac_mute ? CS4362A_MUTE : 0; + cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); + cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); + cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); + cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); + cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); + cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); +} + +static void update_cs43xx_volume(struct oxygen *chip) +{ + cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); + cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); + update_cs4362a_volumes(chip); +} + +static void update_cs43xx_mute(struct oxygen *chip) +{ + u8 reg; + + reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; + if (chip->dac_mute) + reg |= CS4398_MUTE_B | CS4398_MUTE_A; + cs4398_write(chip, 4, reg); + update_cs4362a_volumes(chip); +} + +static void cs43xx_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + /* set CPEN (control port mode) and power down */ + cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + /* configure */ + cs4398_write(chip, 2, data->cs4398_fm); + cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | + CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); + cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | + CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); + cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); + cs4362a_write(chip, 0x05, 0); + cs4362a_write(chip, 0x06, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x0c, data->cs4362a_fm); + update_cs43xx_volume(chip); + update_cs43xx_mute(chip); + /* clear power down */ + cs4398_write(chip, 8, CS4398_CPEN); + cs4362a_write(chip, 0x01, CS4362A_CPEN); +} + +static void xonar_d1_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->generic.anti_pop_delay = 800; + data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE; + data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4362a_fm = CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + cs43xx_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "CS4398"); + snd_component_add(chip->card, "CS4362A"); + snd_component_add(chip->card, "CS5361"); +} + +static void xonar_dx_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPI_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->generic.ext_power_bit = GPI_EXT_POWER; + xonar_init_ext_power(chip); + xonar_d1_init(chip); +} + +static void xonar_d1_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); +} + +static void xonar_d1_suspend(struct oxygen *chip) +{ + xonar_d1_cleanup(chip); +} + +static void xonar_d1_resume(struct oxygen *chip) +{ + oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); + msleep(1); + cs43xx_init(chip); + xonar_enable_output(chip); +} + +static void set_cs43xx_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + if (params_rate(params) <= 50000) { + data->cs4398_fm |= CS4398_FM_SINGLE; + data->cs4362a_fm |= CS4362A_FM_SINGLE; + } else if (params_rate(params) <= 100000) { + data->cs4398_fm |= CS4398_FM_DOUBLE; + data->cs4362a_fm |= CS4362A_FM_DOUBLE; + } else { + data->cs4398_fm |= CS4398_FM_QUAD; + data->cs4362a_fm |= CS4362A_FM_QUAD; + } + cs4398_write(chip, 2, data->cs4398_fm); + cs4362a_write(chip, 0x06, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x0c, data->cs4362a_fm); +} + +static const struct snd_kcontrol_new front_panel_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Panel Switch", + .info = snd_ctl_boolean_mono_info, + .get = xonar_gpio_bit_switch_get, + .put = xonar_gpio_bit_switch_put, + .private_value = GPIO_D1_FRONT_PANEL, +}; + +static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip, + unsigned int reg, unsigned int mute) +{ + if (reg == AC97_LINE) { + spin_lock_irq(&chip->reg_lock); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + mute ? GPIO_D1_INPUT_ROUTE : 0, + GPIO_D1_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + +static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); + +static int xonar_d1_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + return 0; +} + +static int xonar_d1_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); +} + +static const struct oxygen_model model_xonar_d1 = { + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .init = xonar_d1_init, + .control_filter = xonar_d1_control_filter, + .mixer_init = xonar_d1_mixer_init, + .cleanup = xonar_d1_cleanup, + .suspend = xonar_d1_suspend, + .resume = xonar_d1_resume, + .set_dac_params = set_cs43xx_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_cs43xx_volume, + .update_dac_mute = update_cs43xx_mute, + .ac97_switch = xonar_d1_line_mic_ac97_switch, + .dac_tlv = cs4362a_db_scale, + .model_data_size = sizeof(struct xonar_cs43xx), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 8, + .dac_volume_min = 127 - 60, + .dac_volume_max = 127, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int __devinit get_xonar_cs43xx_model(struct oxygen *chip, + const struct pci_device_id *id) +{ + switch (id->subdevice) { + case 0x834f: + chip->model = model_xonar_d1; + chip->model.shortname = "Xonar D1"; + break; + case 0x8275: + case 0x8327: + chip->model = model_xonar_d1; + chip->model.shortname = "Xonar DX"; + chip->model.init = xonar_dx_init; + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c new file mode 100644 index 0000000..b12db1f --- /dev/null +++ b/sound/pci/oxygen/xonar_hdmi.c @@ -0,0 +1,128 @@ +/* + * helper functions for HDMI models (Xonar HDAV1.3) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * 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 driver; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" + +static void hdmi_write_command(struct oxygen *chip, u8 command, + unsigned int count, const u8 *params) +{ + unsigned int i; + u8 checksum; + + oxygen_write_uart(chip, 0xfb); + oxygen_write_uart(chip, 0xef); + oxygen_write_uart(chip, command); + oxygen_write_uart(chip, count); + for (i = 0; i < count; ++i) + oxygen_write_uart(chip, params[i]); + checksum = 0xfb + 0xef + command + count; + for (i = 0; i < count; ++i) + checksum += params[i]; + oxygen_write_uart(chip, checksum); +} + +static void xonar_hdmi_init_commands(struct oxygen *chip, + struct xonar_hdmi *hdmi) +{ + u8 param; + + oxygen_reset_uart(chip); + param = 0; + hdmi_write_command(chip, 0x61, 1, ¶m); + param = 1; + hdmi_write_command(chip, 0x74, 1, ¶m); + hdmi_write_command(chip, 0x54, 5, hdmi->params); +} + +void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi) +{ + hdmi->params[1] = IEC958_AES3_CON_FS_48000; + hdmi->params[4] = 1; + xonar_hdmi_init_commands(chip, hdmi); +} + +void xonar_hdmi_cleanup(struct oxygen *chip) +{ + u8 param = 0; + + hdmi_write_command(chip, 0x74, 1, ¶m); +} + +void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi) +{ + xonar_hdmi_init_commands(chip, hdmi); +} + +void xonar_hdmi_pcm_hardware_filter(unsigned int channel, + struct snd_pcm_hardware *hardware) +{ + if (channel == PCM_MULTICH) { + hardware->rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000; + hardware->rate_min = 44100; + } +} + +void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi, + struct snd_pcm_hw_params *params) +{ + hdmi->params[0] = 0; /* 1 = non-audio */ + switch (params_rate(params)) { + case 44100: + hdmi->params[1] = IEC958_AES3_CON_FS_44100; + break; + case 48000: + hdmi->params[1] = IEC958_AES3_CON_FS_48000; + break; + default: /* 96000 */ + hdmi->params[1] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + hdmi->params[1] = IEC958_AES3_CON_FS_192000; + break; + } + hdmi->params[2] = params_channels(params) / 2 - 1; + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + hdmi->params[3] = 0; + else + hdmi->params[3] = 0xc0; + hdmi->params[4] = 1; /* ? */ + hdmi_write_command(chip, 0x54, 5, hdmi->params); +} + +void xonar_hdmi_uart_input(struct oxygen *chip) +{ + if (chip->uart_input_count >= 2 && + chip->uart_input[chip->uart_input_count - 2] == 'O' && + chip->uart_input[chip->uart_input_count - 1] == 'K') { + printk(KERN_DEBUG "message from HDMI chip received:\n"); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, + chip->uart_input, chip->uart_input_count); + chip->uart_input_count = 0; + } +} diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c new file mode 100644 index 0000000..b3ff713 --- /dev/null +++ b/sound/pci/oxygen/xonar_lib.c @@ -0,0 +1,132 @@ +/* + * helper functions for Asus Xonar cards + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * 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 driver; if not, see . + */ + +#include +#include +#include +#include +#include +#include "xonar.h" + + +#define GPIO_CS53x1_M_MASK 0x000c +#define GPIO_CS53x1_M_SINGLE 0x0000 +#define GPIO_CS53x1_M_DOUBLE 0x0004 +#define GPIO_CS53x1_M_QUAD 0x0008 + + +void xonar_enable_output(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit); + msleep(data->anti_pop_delay); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +void xonar_disable_output(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_ext_power_gpio_changed(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + u8 has_power; + + has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); + if (has_power != data->has_power) { + data->has_power = has_power; + if (has_power) { + snd_printk(KERN_NOTICE "power restored\n"); + } else { + snd_printk(KERN_CRIT + "Hey! Don't unplug the power cable!\n"); + /* TODO: stop PCMs */ + } + } +} + +void xonar_init_ext_power(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_set_bits8(chip, data->ext_power_int_reg, + data->ext_power_bit); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + chip->model.gpio_changed = xonar_ext_power_gpio_changed; + data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); +} + +void xonar_init_cs53x1(struct oxygen *chip) +{ + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); +} + +void xonar_set_cs53x1_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_CS53x1_M_SINGLE; + else if (params_rate(params) <= 108000) + value = GPIO_CS53x1_M_DOUBLE; + else + value = GPIO_CS53x1_M_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_CS53x1_M_MASK); +} + +int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 bit = ctl->private_value; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); + return 0; +} + +int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 bit = ctl->private_value; + u16 old_bits, new_bits; + int changed; + + spin_lock_irq(&chip->reg_lock); + old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_bits = old_bits | bit; + else + new_bits = old_bits & ~bit; + changed = new_bits != old_bits; + if (changed) + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); + spin_unlock_irq(&chip->reg_lock); + return changed; +} diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c new file mode 100644 index 0000000..eb5f015 --- /dev/null +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -0,0 +1,660 @@ +/* + * card driver for models with PCM1796 DACs (Xonar D2/D2X/HDAV1.3/ST/STX) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * 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 driver; if not, see . + */ + +/* + * Xonar D2/D2X + * ------------ + * + * CMI8788: + * + * SPI 0 -> 1st PCM1796 (front) + * SPI 1 -> 2nd PCM1796 (surround) + * SPI 2 -> 3rd PCM1796 (center/LFE) + * SPI 4 -> 4th PCM1796 (back) + * + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 5 <- external power present (D2X only) + * GPIO 7 -> ALT + * GPIO 8 -> enable output to speakers + */ + +/* + * Xonar HDAV1.3 (Deluxe) + * ---------------------- + * + * CMI8788: + * + * I²C <-> PCM1796 (front) + * + * GPI 0 <- external power present + * + * GPIO 0 -> enable output to speakers + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * TXD -> HDMI controller + * RXD <- HDMI controller + * + * PCM1796 front: AD1,0 <- 0,0 + * + * no daughterboard + * ---------------- + * + * GPIO 4 <- 1 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + * + * I²C <-> PCM1796 (surround) + * <-> PCM1796 (center/LFE) + * <-> PCM1796 (back) + * + * PCM1796 surround: AD1,0 <- 0,1 + * PCM1796 center/LFE: AD1,0 <- 1,0 + * PCM1796 back: AD1,0 <- 1,1 + * + * unknown daughterboard + * --------------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 1 + * + * I²C <-> CS4362A (surround, center/LFE, back) + * + * CS4362A: AD0 <- 0 + */ + +/* + * Xonar Essence ST (Deluxe)/STX + * ----------------------------- + * + * CMI8788: + * + * I²C <-> PCM1792A + * + * GPI 0 <- external power present (STX only) + * + * GPIO 0 -> enable output to speakers + * GPIO 1 -> route HP to front panel (0) or rear jack (1) + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 7 -> route output to speaker jacks (0) or HP (1) + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * PCM1792A: + * + * AD1,0 <- 0,0 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" +#include "cm9780.h" +#include "pcm1796.h" + + +#define GPIO_D2X_EXT_POWER 0x0020 +#define GPIO_D2_ALT 0x0080 +#define GPIO_D2_OUTPUT_ENABLE 0x0100 + +#define GPI_EXT_POWER 0x01 +#define GPIO_INPUT_ROUTE 0x0100 + +#define GPIO_HDAV_OUTPUT_ENABLE 0x0001 + +#define GPIO_DB_MASK 0x0030 +#define GPIO_DB_H6 0x0000 + +#define GPIO_ST_OUTPUT_ENABLE 0x0001 +#define GPIO_ST_HP_REAR 0x0002 +#define GPIO_ST_HP 0x0080 + +#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ + + +struct xonar_pcm179x { + struct xonar_generic generic; + unsigned int dacs; + u8 oversampling; +}; + +struct xonar_hdav { + struct xonar_pcm179x pcm179x; + struct xonar_hdmi hdmi; +}; + + +static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + (reg << 8) | value); +} + +static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value); +} + +static void pcm1796_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == + OXYGEN_FUNCTION_SPI) + pcm1796_write_spi(chip, codec, reg, value); + else + pcm1796_write_i2c(chip, codec, reg, value); +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + u8 value; + + value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 18, value); +} + +static void pcm1796_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write(chip, i, 21, 0); + } + update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ + update_pcm1796_volume(chip); +} + +static void xonar_d2_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.anti_pop_delay = 300; + data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE; + data->dacs = 4; + data->oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); + + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_d2x_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPIO_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; + data->generic.ext_power_bit = GPIO_D2X_EXT_POWER; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); + xonar_init_ext_power(chip); + xonar_d2_init(chip); +} + +static void xonar_hdav_init(struct oxygen *chip) +{ + struct xonar_hdav *data = chip->model_data; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + data->pcm179x.generic.anti_pop_delay = 100; + data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE; + data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA; + data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; + data->pcm179x.dacs = chip->model.private_data ? 4 : 1; + data->pcm179x.oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); + + xonar_init_cs53x1(chip); + xonar_init_ext_power(chip); + xonar_hdmi_init(chip, &data->hdmi); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_st_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + data->generic.anti_pop_delay = 100; + data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; + data->dacs = chip->model.private_data ? 4 : 1; + data->oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1792A"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_stx_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPI_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->generic.ext_power_bit = GPI_EXT_POWER; + xonar_init_ext_power(chip); + xonar_st_init(chip); +} + +static void xonar_d2_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + +static void xonar_hdav_cleanup(struct oxygen *chip) +{ + xonar_hdmi_cleanup(chip); + xonar_disable_output(chip); + msleep(2); +} + +static void xonar_st_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + +static void xonar_d2_suspend(struct oxygen *chip) +{ + xonar_d2_cleanup(chip); +} + +static void xonar_hdav_suspend(struct oxygen *chip) +{ + xonar_hdav_cleanup(chip); +} + +static void xonar_st_suspend(struct oxygen *chip) +{ + xonar_st_cleanup(chip); +} + +static void xonar_d2_resume(struct oxygen *chip) +{ + pcm1796_init(chip); + xonar_enable_output(chip); +} + +static void xonar_hdav_resume(struct oxygen *chip) +{ + struct xonar_hdav *data = chip->model_data; + + pcm1796_init(chip); + xonar_hdmi_resume(chip, &data->hdmi); + xonar_enable_output(chip); +} + +static void xonar_st_resume(struct oxygen *chip) +{ + pcm1796_init(chip); + xonar_enable_output(chip); +} + +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + data->oversampling = + params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 20, data->oversampling); +} + +static void set_hdav_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_hdav *data = chip->model_data; + + set_pcm1796_params(chip, params); + xonar_set_hdmi_params(chip, &data->hdmi, params); +} + +static const struct snd_kcontrol_new alt_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Loopback Switch", + .info = snd_ctl_boolean_mono_info, + .get = xonar_gpio_bit_switch_get, + .put = xonar_gpio_bit_switch_put, + .private_value = GPIO_D2_ALT, +}; + +static int st_output_switch_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Speakers", "Headphones", "FP Headphones" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item >= 3) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int st_output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio; + + gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (!(gpio & GPIO_ST_HP)) + value->value.enumerated.item[0] = 0; + else if (gpio & GPIO_ST_HP_REAR) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + return 0; +} + + +static int st_output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio_old, gpio; + + mutex_lock(&chip->mutex); + gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); + gpio = gpio_old; + switch (value->value.enumerated.item[0]) { + case 0: + gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR); + break; + case 1: + gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR; + break; + case 2: + gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR; + break; + } + oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + mutex_unlock(&chip->mutex); + return gpio != gpio_old; +} + +static const struct snd_kcontrol_new st_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = st_output_switch_get, + .put = st_output_switch_put, +}; + +static void xonar_line_mic_ac97_switch(struct oxygen *chip, + unsigned int reg, unsigned int mute) +{ + if (reg == AC97_LINE) { + spin_lock_irq(&chip->reg_lock); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + mute ? GPIO_INPUT_ROUTE : 0, + GPIO_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + +static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0); + +static int xonar_d2_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + /* CD in is actually connected to the video in pin */ + template->private_value ^= AC97_CD ^ AC97_VIDEO; + return 0; +} + +static int xonar_st_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + if (!strcmp(template->name, "Stereo Upmixing")) + return 1; /* stereo only - we don't need upmixing */ + return 0; +} + +static int xonar_d2_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); +} + +static int xonar_st_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); +} + +static const struct oxygen_model model_xonar_d2 = { + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_d2_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_d2_mixer_init, + .cleanup = xonar_d2_cleanup, + .suspend = xonar_d2_suspend, + .resume = xonar_d2_resume, + .set_dac_params = set_pcm1796_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_pcm179x), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF | + MIDI_OUTPUT | + MIDI_INPUT, + .dac_channels = 8, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static const struct oxygen_model model_xonar_hdav = { + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_hdav_init, + .cleanup = xonar_hdav_cleanup, + .suspend = xonar_hdav_suspend, + .resume = xonar_hdav_resume, + .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, + .set_dac_params = set_hdav_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .uart_input = xonar_hdmi_uart_input, + .ac97_switch = xonar_line_mic_ac97_switch, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_hdav), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static const struct oxygen_model model_xonar_st = { + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .init = xonar_st_init, + .control_filter = xonar_st_control_filter, + .mixer_init = xonar_st_mixer_init, + .cleanup = xonar_st_cleanup, + .suspend = xonar_st_suspend, + .resume = xonar_st_resume, + .set_dac_params = set_pcm1796_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .ac97_switch = xonar_line_mic_ac97_switch, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_pcm179x), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 2, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int __devinit get_xonar_pcm179x_model(struct oxygen *chip, + const struct pci_device_id *id) +{ + switch (id->subdevice) { + case 0x8269: + chip->model = model_xonar_d2; + chip->model.shortname = "Xonar D2"; + break; + case 0x82b7: + chip->model = model_xonar_d2; + chip->model.shortname = "Xonar D2X"; + chip->model.init = xonar_d2x_init; + break; + case 0x8314: + chip->model = model_xonar_hdav; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + default: + chip->model.shortname = "Xonar HDAV1.3"; + break; + case GPIO_DB_H6: + chip->model.shortname = "Xonar HDAV1.3+H6"; + chip->model.private_data = 1; + break; + } + break; + case 0x835d: + chip->model = model_xonar_st; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + default: + chip->model.shortname = "Xonar ST"; + break; + case GPIO_DB_H6: + chip->model.shortname = "Xonar ST+H6"; + chip->model.dac_channels = 8; + chip->model.private_data = 1; + break; + } + break; + case 0x835c: + chip->model = model_xonar_st; + chip->model.shortname = "Xonar STX"; + chip->model.init = xonar_stx_init; + break; + default: + return -EINVAL; + } + return 0; +} -- cgit v0.10.2 From 268304f4c4f0b8677d67400f04ad4e0271ec3742 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:15:01 +0200 Subject: sound: virtuoso: fix Xonar Essence ST support The Essence ST uses the CS2000 chip to generate the DAC master clock, so we better initialize and program it appropriately. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/cs2000.h b/sound/pci/oxygen/cs2000.h new file mode 100644 index 0000000..c3501bd --- /dev/null +++ b/sound/pci/oxygen/cs2000.h @@ -0,0 +1,83 @@ +#ifndef CS2000_H_INCLUDED +#define CS2000_H_INCLUDED + +#define CS2000_DEV_ID 0x01 +#define CS2000_DEV_CTRL 0x02 +#define CS2000_DEV_CFG_1 0x03 +#define CS2000_DEV_CFG_2 0x04 +#define CS2000_GLOBAL_CFG 0x05 +#define CS2000_RATIO_0 0x06 /* 32 bits, big endian */ +#define CS2000_RATIO_1 0x0a +#define CS2000_RATIO_2 0x0e +#define CS2000_RATIO_3 0x12 +#define CS2000_FUN_CFG_1 0x16 +#define CS2000_FUN_CFG_2 0x17 +#define CS2000_FUN_CFG_3 0x1e + +/* DEV_ID */ +#define CS2000_DEVICE_MASK 0xf8 +#define CS2000_REVISION_MASK 0x07 + +/* DEV_CTRL */ +#define CS2000_UNLOCK 0x80 +#define CS2000_AUX_OUT_DIS 0x02 +#define CS2000_CLK_OUT_DIS 0x01 + +/* DEV_CFG_1 */ +#define CS2000_R_MOD_SEL_MASK 0xe0 +#define CS2000_R_MOD_SEL_1 0x00 +#define CS2000_R_MOD_SEL_2 0x20 +#define CS2000_R_MOD_SEL_4 0x40 +#define CS2000_R_MOD_SEL_8 0x60 +#define CS2000_R_MOD_SEL_1_2 0x80 +#define CS2000_R_MOD_SEL_1_4 0xa0 +#define CS2000_R_MOD_SEL_1_8 0xc0 +#define CS2000_R_MOD_SEL_1_16 0xe0 +#define CS2000_R_SEL_MASK 0x18 +#define CS2000_R_SEL_SHIFT 3 +#define CS2000_AUX_OUT_SRC_MASK 0x06 +#define CS2000_AUX_OUT_SRC_REF_CLK 0x00 +#define CS2000_AUX_OUT_SRC_CLK_IN 0x02 +#define CS2000_AUX_OUT_SRC_CLK_OUT 0x04 +#define CS2000_AUX_OUT_SRC_PLL_LOCK 0x06 +#define CS2000_EN_DEV_CFG_1 0x01 + +/* DEV_CFG_2 */ +#define CS2000_LOCK_CLK_MASK 0x06 +#define CS2000_LOCK_CLK_SHIFT 1 +#define CS2000_FRAC_N_SRC_MASK 0x01 +#define CS2000_FRAC_N_SRC_STATIC 0x00 +#define CS2000_FRAC_N_SRC_DYNAMIC 0x01 + +/* GLOBAL_CFG */ +#define CS2000_FREEZE 0x08 +#define CS2000_EN_DEV_CFG_2 0x01 + +/* FUN_CFG_1 */ +#define CS2000_CLK_SKIP_EN 0x80 +#define CS2000_AUX_LOCK_CFG_MASK 0x40 +#define CS2000_AUX_LOCK_CFG_PP_HIGH 0x00 +#define CS2000_AUX_LOCK_CFG_OD_LOW 0x40 +#define CS2000_REF_CLK_DIV_MASK 0x18 +#define CS2000_REF_CLK_DIV_4 0x00 +#define CS2000_REF_CLK_DIV_2 0x08 +#define CS2000_REF_CLK_DIV_1 0x10 + +/* FUN_CFG_2 */ +#define CS2000_CLK_OUT_UNL 0x10 +#define CS2000_L_F_RATIO_CFG_MASK 0x08 +#define CS2000_L_F_RATIO_CFG_20_12 0x00 +#define CS2000_L_F_RATIO_CFG_12_20 0x08 + +/* FUN_CFG_3 */ +#define CS2000_CLK_IN_BW_MASK 0x70 +#define CS2000_CLK_IN_BW_1 0x00 +#define CS2000_CLK_IN_BW_2 0x10 +#define CS2000_CLK_IN_BW_4 0x20 +#define CS2000_CLK_IN_BW_8 0x30 +#define CS2000_CLK_IN_BW_16 0x40 +#define CS2000_CLK_IN_BW_32 0x50 +#define CS2000_CLK_IN_BW_64 0x60 +#define CS2000_CLK_IN_BW_128 0x70 + +#endif diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index eb5f015..522efde 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -91,6 +91,9 @@ * CMI8788: * * I²C <-> PCM1792A + * <-> CS2000 (ST only) + * + * ADC1 MCLK -> REF_CLK of CS2000 (ST only) * * GPI 0 <- external power present (STX only) * @@ -124,6 +127,7 @@ #include "xonar.h" #include "cm9780.h" #include "pcm1796.h" +#include "cs2000.h" #define GPIO_D2X_EXT_POWER 0x0020 @@ -143,12 +147,14 @@ #define GPIO_ST_HP 0x0080 #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ +#define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 oversampling; + u8 cs2000_fun_cfg_1; }; struct xonar_hdav { @@ -188,6 +194,11 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec, pcm1796_write_i2c(chip, codec, reg, value); } +static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); +} + static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; @@ -292,14 +303,17 @@ static void xonar_hdav_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } -static void xonar_st_init(struct oxygen *chip) +static void xonar_st_init_i2c(struct oxygen *chip) { - struct xonar_pcm179x *data = chip->model_data; - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_SPEED_FAST); +} + +static void xonar_st_init_common(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; @@ -320,15 +334,57 @@ static void xonar_st_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } +static void cs2000_registers_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_FREEZE); + cs2000_write(chip, CS2000_DEV_CTRL, 0); + cs2000_write(chip, CS2000_DEV_CFG_1, + CS2000_R_MOD_SEL_1 | + (0 << CS2000_R_SEL_SHIFT) | + CS2000_AUX_OUT_SRC_REF_CLK | + CS2000_EN_DEV_CFG_1); + cs2000_write(chip, CS2000_DEV_CFG_2, + (0 << CS2000_LOCK_CLK_SHIFT) | + CS2000_FRAC_N_SRC_STATIC); + cs2000_write(chip, CS2000_RATIO_0 + 0, 0x00); /* 1.0 */ + cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10); + cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00); + cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00); + cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); + cs2000_write(chip, CS2000_FUN_CFG_2, 0); + cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2); +} + +static void xonar_st_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + + xonar_st_init_i2c(chip); + cs2000_registers_init(chip); + xonar_st_init_common(chip); + + snd_component_add(chip->card, "CS2000"); +} + static void xonar_stx_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + xonar_st_init_i2c(chip); data->generic.ext_power_reg = OXYGEN_GPI_DATA; data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->generic.ext_power_bit = GPI_EXT_POWER; xonar_init_ext_power(chip); - xonar_st_init(chip); + xonar_st_init_common(chip); } static void xonar_d2_cleanup(struct oxygen *chip) @@ -378,12 +434,18 @@ static void xonar_hdav_resume(struct oxygen *chip) xonar_enable_output(chip); } -static void xonar_st_resume(struct oxygen *chip) +static void xonar_stx_resume(struct oxygen *chip) { pcm1796_init(chip); xonar_enable_output(chip); } +static void xonar_st_resume(struct oxygen *chip) +{ + cs2000_registers_init(chip); + xonar_stx_resume(chip); +} + static void set_pcm1796_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { @@ -396,6 +458,43 @@ static void set_pcm1796_params(struct oxygen *chip, pcm1796_write(chip, i, 20, data->oversampling); } +static void set_cs2000_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + /* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */ + static const u8 rate_mclks[] = { + [OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, + }; + struct xonar_pcm179x *data = chip->model_data; + unsigned int rate_index; + u8 rate_mclk; + + rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) + & OXYGEN_I2S_RATE_MASK; + rate_mclk = rate_mclks[rate_index]; + oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, + OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); + if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + else + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_2; + cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); +} + +static void set_st_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + set_cs2000_params(chip, params); + set_pcm1796_params(chip, params); +} + static void set_hdav_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { @@ -590,7 +689,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, - .set_dac_params = set_pcm1796_params, + .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, .update_dac_mute = update_pcm1796_mute, @@ -652,6 +751,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model = model_xonar_st; chip->model.shortname = "Xonar STX"; chip->model.init = xonar_stx_init; + chip->model.resume = xonar_stx_resume; + chip->model.set_dac_params = set_pcm1796_params; break; default: return -EINVAL; -- cgit v0.10.2 From 75919d7c057be888c7cd7b192fad02182260b04a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:15:49 +0200 Subject: sound: oxygen: better defaults for upmixing control On card models with two-channel outputs, the base driver can automatically disable the upmixing control so that the model drivers do not need to do this. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 84ef131..9026a14 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -141,19 +141,11 @@ static void set_cs5340_params(struct oxygen *chip, static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); -static int hifier_control_filter(struct snd_kcontrol_new *template) -{ - if (!strcmp(template->name, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ - return 0; -} - static const struct oxygen_model model_hifier = { .shortname = "C-Media CMI8787", .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = hifier_init, - .control_filter = hifier_control_filter, .cleanup = hifier_cleanup, .resume = hifier_resume, .set_dac_params = set_ak4396_params, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 5401c54..e8e911a 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -954,6 +954,9 @@ static int add_controls(struct oxygen *chip, if (err == 1) continue; } + if (!strcmp(template.name, "Stereo Upmixing") && + chip->model.dac_channels == 2) + continue; if (!strcmp(template.name, "Master Playback Volume") && chip->model.dac_tlv) { template.tlv.p = chip->model.dac_tlv; diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 522efde..07aaa89 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -605,8 +605,6 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) { if (!strncmp(template->name, "CD Capture ", 11)) return 1; /* no CD input */ - if (!strcmp(template->name, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ return 0; } -- cgit v0.10.2 From 3d8bb454c4fbe18cea1adfd4183a4a9ef5f0ef04 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:16:41 +0200 Subject: sound: oxygen: add stereo upmixing to center/LFE channels Add the possibility to route a mix of the two channels of stereo data to the center and LFE outputs. This is implemented only for models where the DACs support this, i.e., for the Xonar D1 and DX. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index bd615db..2ac3b3c 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -84,6 +84,7 @@ struct oxygen_model { struct snd_pcm_hw_params *params); void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); + void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed); void (*gpio_changed)(struct oxygen *chip); void (*uart_input)(struct oxygen *chip); void (*ac97_switch)(struct oxygen *chip, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index e8e911a..5dfb5fb 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -99,11 +99,15 @@ static int dac_mute_put(struct snd_kcontrol *ctl, static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { - static const char *const names[3] = { - "Front", "Front+Surround", "Front+Surround+Back" + static const char *const names[5] = { + "Front", + "Front+Surround", + "Front+Surround+Back", + "Front+Surround+Center/LFE", + "Front+Surround+Center/LFE+Back", }; struct oxygen *chip = ctl->private_data; - unsigned int count = 2 + (chip->model.dac_channels == 8); + unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; info->count = 1; @@ -127,7 +131,7 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) void oxygen_update_dac_routing(struct oxygen *chip) { /* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */ - static const unsigned int reg_values[3] = { + static const unsigned int reg_values[5] = { /* stereo -> front */ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | @@ -143,6 +147,16 @@ void oxygen_update_dac_routing(struct oxygen *chip) (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+center/LFE */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+center/LFE+back */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), }; u8 channels; unsigned int reg_value; @@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip) OXYGEN_PLAY_DAC1_SOURCE_MASK | OXYGEN_PLAY_DAC2_SOURCE_MASK | OXYGEN_PLAY_DAC3_SOURCE_MASK); + if (chip->model.update_center_lfe_mix) + chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2); } static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; - unsigned int count = 2 + (chip->model.dac_channels == 8); + unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; int changed; + if (value->value.enumerated.item[0] >= count) + return -EINVAL; mutex_lock(&chip->mutex); changed = value->value.enumerated.item[0] != chip->dac_routing; if (changed) { - chip->dac_routing = min(value->value.enumerated.item[0], - count - 1); - spin_lock_irq(&chip->reg_lock); + chip->dac_routing = value->value.enumerated.item[0]; oxygen_update_dac_routing(chip); - spin_unlock_irq(&chip->reg_lock); } mutex_unlock(&chip->mutex); return changed; diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index ef2345d..1e98333 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -435,6 +435,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + mutex_lock(&chip->mutex); spin_lock_irq(&chip->reg_lock); oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, OXYGEN_SPDIF_OUT_ENABLE); @@ -446,6 +447,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, OXYGEN_SPDIF_OUT_RATE_MASK); oxygen_update_spdif_source(chip); spin_unlock_irq(&chip->reg_lock); + mutex_unlock(&chip->mutex); return 0; } @@ -459,6 +461,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + mutex_lock(&chip->mutex); spin_lock_irq(&chip->reg_lock); oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, oxygen_play_channels(hw_params), @@ -475,12 +478,11 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, OXYGEN_I2S_FORMAT_MASK | OXYGEN_I2S_MCLK_MASK | OXYGEN_I2S_BITS_MASK); - oxygen_update_dac_routing(chip); oxygen_update_spdif_source(chip); spin_unlock_irq(&chip->reg_lock); - mutex_lock(&chip->mutex); chip->model.set_dac_params(chip, hw_params); + oxygen_update_dac_routing(chip); mutex_unlock(&chip->mutex); return 0; } diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 8fb5797..0fa05ed 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -67,6 +67,7 @@ struct xonar_cs43xx { struct xonar_generic generic; u8 cs4398_fm; u8 cs4362a_fm; + u8 cs4362a_fm_c; }; static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) @@ -128,7 +129,7 @@ static void cs43xx_init(struct oxygen *chip) cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); cs4362a_write(chip, 0x05, 0); cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); cs4362a_write(chip, 0x0c, data->cs4362a_fm); update_cs43xx_volume(chip); update_cs43xx_mute(chip); @@ -146,6 +147,7 @@ static void xonar_d1_init(struct oxygen *chip) data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; data->cs4362a_fm = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + data->cs4362a_fm_c = data->cs4362a_fm; oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | @@ -202,25 +204,41 @@ static void set_cs43xx_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct xonar_cs43xx *data = chip->model_data; + u8 cs4398_fm, cs4362a_fm; - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; if (params_rate(params) <= 50000) { - data->cs4398_fm |= CS4398_FM_SINGLE; - data->cs4362a_fm |= CS4362A_FM_SINGLE; + cs4398_fm = CS4398_FM_SINGLE; + cs4362a_fm = CS4362A_FM_SINGLE; } else if (params_rate(params) <= 100000) { - data->cs4398_fm |= CS4398_FM_DOUBLE; - data->cs4362a_fm |= CS4362A_FM_DOUBLE; + cs4398_fm = CS4398_FM_DOUBLE; + cs4362a_fm = CS4362A_FM_DOUBLE; } else { - data->cs4398_fm |= CS4398_FM_QUAD; - data->cs4362a_fm |= CS4362A_FM_QUAD; + cs4398_fm = CS4398_FM_QUAD; + cs4362a_fm = CS4362A_FM_QUAD; } + data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST | cs4398_fm; + data->cs4362a_fm = + (data->cs4362a_fm & ~CS4362A_FM_MASK) | cs4362a_fm; + data->cs4362a_fm_c = + (data->cs4362a_fm_c & ~CS4362A_FM_MASK) | cs4362a_fm; cs4398_write(chip, 2, data->cs4398_fm); cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); cs4362a_write(chip, 0x0c, data->cs4362a_fm); } +static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->cs4362a_fm_c &= ~CS4362A_ATAPI_MASK; + if (mixed) + data->cs4362a_fm_c |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; + else + data->cs4362a_fm_c |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); +} + static const struct snd_kcontrol_new front_panel_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Front Panel Switch", @@ -269,6 +287,7 @@ static const struct oxygen_model model_xonar_d1 = { .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_cs43xx_volume, .update_dac_mute = update_cs43xx_mute, + .update_center_lfe_mix = update_cs43xx_center_lfe_mix, .ac97_switch = xonar_d1_line_mic_ac97_switch, .dac_tlv = cs4362a_db_scale, .model_data_size = sizeof(struct xonar_cs43xx), -- cgit v0.10.2 From dc0adf48daa81b05765d3c5ebab76321f77e9d21 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:17:36 +0200 Subject: sound: oxygen: more hardware documentation Add some comments describing the hardware pin routing. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 9026a14..19e9e01 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -17,6 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * CMI8788: + * + * SPI 0 -> AK4396 + */ + #include #include #include diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 72db4c3..53dff71 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -18,6 +18,8 @@ */ /* + * CMI8788: + * * SPI 0 -> 1st AK4396 (front) * SPI 1 -> 2nd AK4396 (surround) * SPI 2 -> 3rd AK4396 (center/LFE) @@ -27,6 +29,10 @@ * GPIO 0 -> DFS0 of AK5385 * GPIO 1 -> DFS1 of AK5385 * GPIO 8 -> enable headphone amplifier on HT-Omega models + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input */ #include diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 0fa05ed..a8ec4e8 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -41,6 +41,10 @@ * CS4362A: * * AD0 <- 0 + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input */ #include diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 07aaa89..97574db 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -32,6 +32,10 @@ * GPIO 5 <- external power present (D2X only) * GPIO 7 -> ALT * GPIO 8 -> enable output to speakers + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input */ /* @@ -54,6 +58,10 @@ * * PCM1796 front: AD1,0 <- 0,0 * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + * * no daughterboard * ---------------- * @@ -107,6 +115,15 @@ * PCM1792A: * * AD1,0 <- 0,0 + * SCK <- CLK_OUT of CS2000 (ST only) + * + * CS2000: + * + * AD0 <- 0 + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input * * H6 daughterboard * ---------------- -- cgit v0.10.2 From 6f0de3ce068e48b033b5e4d0822b47218e9d206c Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:18:45 +0200 Subject: sound: oxygen: cache codec registers Keep a cache of codec registers to avoid unnecessary writes. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 19e9e01..2079c10 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -57,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = { MODULE_DEVICE_TABLE(pci, hifier_ids); struct hifier_data { - u8 ak4396_ctl2; + u8 ak4396_regs[5]; }; static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) { + struct hifier_data *data = chip->model_data; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_CLOCK_160 | (0 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, AK4396_WRITE | (reg << 8) | value); + data->ak4396_regs[reg] = value; } -static void update_ak4396_volume(struct oxygen *chip) +static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value) { - ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); - ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); + struct hifier_data *data = chip->model_data; + + if (value != data->ak4396_regs[reg]) + ak4396_write(chip, reg, value); } static void hifier_registers_init(struct oxygen *chip) @@ -81,16 +86,19 @@ static void hifier_registers_init(struct oxygen *chip) struct hifier_data *data = chip->model_data; ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); - ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2); + ak4396_write(chip, AK4396_CONTROL_2, + data->ak4396_regs[AK4396_CONTROL_2]); ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); - update_ak4396_volume(chip); + ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); } static void hifier_init(struct oxygen *chip) { struct hifier_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_regs[AK4396_CONTROL_2] = + AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; hifier_registers_init(chip); snd_component_add(chip->card, "AK4396"); @@ -112,20 +120,29 @@ static void set_ak4396_params(struct oxygen *chip, struct hifier_data *data = chip->model_data; u8 value; - value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK; if (params_rate(params) <= 54000) value |= AK4396_DFS_NORMAL; else if (params_rate(params) <= 108000) value |= AK4396_DFS_DOUBLE; else value |= AK4396_DFS_QUAD; - data->ak4396_ctl2 = value; msleep(1); /* wait for the new MCLK to become stable */ - ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB); - ak4396_write(chip, AK4396_CONTROL_2, value); - ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + if (value != data->ak4396_regs[AK4396_CONTROL_2]) { + ak4396_write(chip, AK4396_CONTROL_1, + AK4396_DIF_24_MSB); + ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write(chip, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]); } static void update_ak4396_mute(struct oxygen *chip) @@ -133,11 +150,10 @@ static void update_ak4396_mute(struct oxygen *chip) struct hifier_data *data = chip->model_data; u8 value; - value = data->ak4396_ctl2 & ~AK4396_SMUTE; + value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE; if (chip->dac_mute) value |= AK4396_SMUTE; - data->ak4396_ctl2 = value; - ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write_cached(chip, AK4396_CONTROL_2, value); } static void set_cs5340_params(struct oxygen *chip, diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 53dff71..c986c5e 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -97,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #define GPIO_CLARO_HP 0x0100 struct generic_data { - u8 ak4396_ctl2; - u16 saved_wm8785_registers[2]; + u8 ak4396_regs[4][5]; + u16 wm8785_regs[1]; }; static void ak4396_write(struct oxygen *chip, unsigned int codec, @@ -108,12 +108,24 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec, static const u8 codec_spi_map[4] = { 0, 1, 2, 4 }; + struct generic_data *data = chip->model_data; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_CLOCK_160 | (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, AK4396_WRITE | (reg << 8) | value); + data->ak4396_regs[codec][reg] = value; +} + +static void ak4396_write_cached(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + struct generic_data *data = chip->model_data; + + if (value != data->ak4396_regs[codec][reg]) + ak4396_write(chip, codec, reg, value); } static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) @@ -126,20 +138,8 @@ static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) (3 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, (reg << 9) | value); - if (reg < ARRAY_SIZE(data->saved_wm8785_registers)) - data->saved_wm8785_registers[reg] = value; -} - -static void update_ak4396_volume(struct oxygen *chip) -{ - unsigned int i; - - for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_LCH_ATT, chip->dac_volume[i * 2]); - ak4396_write(chip, i, - AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]); - } + if (reg < ARRAY_SIZE(data->wm8785_regs)) + data->wm8785_regs[reg] = value; } static void ak4396_registers_init(struct oxygen *chip) @@ -148,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip) unsigned int i; for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); - ak4396_write(chip, i, - AK4396_CONTROL_2, data->ak4396_ctl2); - ak4396_write(chip, i, - AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, i, AK4396_CONTROL_2, + data->ak4396_regs[0][AK4396_CONTROL_2]); + ak4396_write(chip, i, AK4396_CONTROL_3, + AK4396_PCM); + ak4396_write(chip, i, AK4396_LCH_ATT, + chip->dac_volume[i * 2]); + ak4396_write(chip, i, AK4396_RCH_ATT, + chip->dac_volume[i * 2 + 1]); } - update_ak4396_volume(chip); } static void ak4396_init(struct oxygen *chip) { struct generic_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_regs[0][AK4396_CONTROL_2] = + AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; ak4396_registers_init(chip); snd_component_add(chip->card, "AK4396"); } @@ -179,17 +183,15 @@ static void wm8785_registers_init(struct oxygen *chip) struct generic_data *data = chip->model_data; wm8785_write(chip, WM8785_R7, 0); - wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]); - wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]); + wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]); } static void wm8785_init(struct oxygen *chip) { struct generic_data *data = chip->model_data; - data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE | - WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; - data->saved_wm8785_registers[1] = WM8785_WL_24; + data->wm8785_regs[0] = + WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; wm8785_registers_init(chip); snd_component_add(chip->card, "WM8785"); } @@ -270,24 +272,36 @@ static void set_ak4396_params(struct oxygen *chip, unsigned int i; u8 value; - value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK; if (params_rate(params) <= 54000) value |= AK4396_DFS_NORMAL; else if (params_rate(params) <= 108000) value |= AK4396_DFS_DOUBLE; else value |= AK4396_DFS_QUAD; - data->ak4396_ctl2 = value; msleep(1); /* wait for the new MCLK to become stable */ + if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) { + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB); + ak4396_write(chip, i, AK4396_CONTROL_2, value); + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + } + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + unsigned int i; + for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB); - ak4396_write(chip, i, - AK4396_CONTROL_2, value); - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write_cached(chip, i, AK4396_LCH_ATT, + chip->dac_volume[i * 2]); + ak4396_write_cached(chip, i, AK4396_RCH_ATT, + chip->dac_volume[i * 2 + 1]); } } @@ -297,21 +311,19 @@ static void update_ak4396_mute(struct oxygen *chip) unsigned int i; u8 value; - value = data->ak4396_ctl2 & ~AK4396_SMUTE; + value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE; if (chip->dac_mute) value |= AK4396_SMUTE; - data->ak4396_ctl2 = value; for (i = 0; i < 4; ++i) - ak4396_write(chip, i, AK4396_CONTROL_2, value); + ak4396_write_cached(chip, i, AK4396_CONTROL_2, value); } static void set_wm8785_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { + struct generic_data *data = chip->model_data; unsigned int value; - wm8785_write(chip, WM8785_R7, 0); - value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; if (params_rate(params) <= 48000) value |= WM8785_OSR_SINGLE; @@ -319,13 +331,10 @@ static void set_wm8785_params(struct oxygen *chip, value |= WM8785_OSR_DOUBLE; else value |= WM8785_OSR_QUAD; - wm8785_write(chip, WM8785_R0, value); - - if (snd_pcm_format_width(params_format(params)) <= 16) - value = WM8785_WL_16; - else - value = WM8785_WL_24; - wm8785_write(chip, WM8785_R1, value); + if (value != data->wm8785_regs[0]) { + wm8785_write(chip, WM8785_R7, 0); + wm8785_write(chip, WM8785_R0, value); + } } static void set_ak5385_params(struct oxygen *chip, diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index a8ec4e8..330c5e7 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -69,62 +69,58 @@ struct xonar_cs43xx { struct xonar_generic generic; - u8 cs4398_fm; - u8 cs4362a_fm; - u8 cs4362a_fm_c; + u8 cs4398_regs[7]; + u8 cs4362a_regs[15]; }; static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) { - oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); -} + struct xonar_cs43xx *data = chip->model_data; -static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); + oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); + if (reg < ARRAY_SIZE(data->cs4398_regs)) + data->cs4398_regs[reg] = value; } -static void update_cs4362a_volumes(struct oxygen *chip) +static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value) { - u8 mute; + struct xonar_cs43xx *data = chip->model_data; - mute = chip->dac_mute ? CS4362A_MUTE : 0; - cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); - cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); - cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); - cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); - cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); - cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); + if (value != data->cs4398_regs[reg]) + cs4398_write(chip, reg, value); } -static void update_cs43xx_volume(struct oxygen *chip) +static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) { - cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); - cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); - update_cs4362a_volumes(chip); + struct xonar_cs43xx *data = chip->model_data; + + oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); + if (reg < ARRAY_SIZE(data->cs4362a_regs)) + data->cs4362a_regs[reg] = value; } -static void update_cs43xx_mute(struct oxygen *chip) +static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value) { - u8 reg; + struct xonar_cs43xx *data = chip->model_data; - reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; - if (chip->dac_mute) - reg |= CS4398_MUTE_B | CS4398_MUTE_A; - cs4398_write(chip, 4, reg); - update_cs4362a_volumes(chip); + if (value != data->cs4362a_regs[reg]) + cs4362a_write(chip, reg, value); } -static void cs43xx_init(struct oxygen *chip) +static void cs43xx_registers_init(struct oxygen *chip) { struct xonar_cs43xx *data = chip->model_data; + unsigned int i; /* set CPEN (control port mode) and power down */ cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); /* configure */ - cs4398_write(chip, 2, data->cs4398_fm); + cs4398_write(chip, 2, data->cs4398_regs[2]); cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + cs4398_write(chip, 4, data->cs4398_regs[4]); + cs4398_write(chip, 5, data->cs4398_regs[5]); + cs4398_write(chip, 6, data->cs4398_regs[6]); cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); @@ -132,11 +128,8 @@ static void cs43xx_init(struct oxygen *chip) CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); cs4362a_write(chip, 0x05, 0); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); - update_cs43xx_volume(chip); - update_cs43xx_mute(chip); + for (i = 6; i <= 14; ++i) + cs4362a_write(chip, i, data->cs4362a_regs[i]); /* clear power down */ cs4398_write(chip, 8, CS4398_CPEN); cs4362a_write(chip, 0x01, CS4362A_CPEN); @@ -148,17 +141,29 @@ static void xonar_d1_init(struct oxygen *chip) data->generic.anti_pop_delay = 800; data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE; - data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_FM_SINGLE | + data->cs4398_regs[2] = + CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4398_regs[4] = CS4398_MUTEP_LOW | + CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE; + data->cs4398_regs[5] = 60 * 2; + data->cs4398_regs[6] = 60 * 2; + data->cs4362a_regs[6] = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - data->cs4362a_fm_c = data->cs4362a_fm; + data->cs4362a_regs[7] = 60 | CS4362A_MUTE; + data->cs4362a_regs[8] = 60 | CS4362A_MUTE; + data->cs4362a_regs[9] = data->cs4362a_regs[6]; + data->cs4362a_regs[10] = 60 | CS4362A_MUTE; + data->cs4362a_regs[11] = 60 | CS4362A_MUTE; + data->cs4362a_regs[12] = data->cs4362a_regs[6]; + data->cs4362a_regs[13] = 60 | CS4362A_MUTE; + data->cs4362a_regs[14] = 60 | CS4362A_MUTE; oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_SPEED_FAST); - cs43xx_init(chip); + cs43xx_registers_init(chip); oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); @@ -200,7 +205,7 @@ static void xonar_d1_resume(struct oxygen *chip) { oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); msleep(1); - cs43xx_init(chip); + cs43xx_registers_init(chip); xonar_enable_output(chip); } @@ -220,27 +225,56 @@ static void set_cs43xx_params(struct oxygen *chip, cs4398_fm = CS4398_FM_QUAD; cs4362a_fm = CS4362A_FM_QUAD; } - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST | cs4398_fm; - data->cs4362a_fm = - (data->cs4362a_fm & ~CS4362A_FM_MASK) | cs4362a_fm; - data->cs4362a_fm_c = - (data->cs4362a_fm_c & ~CS4362A_FM_MASK) | cs4362a_fm; - cs4398_write(chip, 2, data->cs4398_fm); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); + cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST; + cs4398_write_cached(chip, 2, cs4398_fm); + cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK; + cs4362a_write_cached(chip, 6, cs4362a_fm); + cs4362a_write_cached(chip, 12, cs4362a_fm); + cs4362a_fm &= CS4362A_FM_MASK; + cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK; + cs4362a_write_cached(chip, 9, cs4362a_fm); +} + +static void update_cs4362a_volumes(struct oxygen *chip) +{ + unsigned int i; + u8 mute; + + mute = chip->dac_mute ? CS4362A_MUTE : 0; + for (i = 0; i < 6; ++i) + cs4362a_write_cached(chip, 7 + i + i / 2, + (127 - chip->dac_volume[2 + i]) | mute); +} + +static void update_cs43xx_volume(struct oxygen *chip) +{ + cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2); + cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2); + update_cs4362a_volumes(chip); +} + +static void update_cs43xx_mute(struct oxygen *chip) +{ + u8 reg; + + reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; + if (chip->dac_mute) + reg |= CS4398_MUTE_B | CS4398_MUTE_A; + cs4398_write_cached(chip, 4, reg); + update_cs4362a_volumes(chip); } static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed) { struct xonar_cs43xx *data = chip->model_data; + u8 reg; - data->cs4362a_fm_c &= ~CS4362A_ATAPI_MASK; + reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK; if (mixed) - data->cs4362a_fm_c |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; + reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; else - data->cs4362a_fm_c |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); + reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + cs4362a_write_cached(chip, 9, reg); } static const struct snd_kcontrol_new front_panel_switch = { diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 97574db..e17ee5e 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -166,11 +166,13 @@ #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ #define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */ +#define PCM1796_REG_BASE 16 + struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; - u8 oversampling; + u8 pcm1796_regs[4][5]; u8 cs2000_fun_cfg_1; }; @@ -204,54 +206,71 @@ static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, static void pcm1796_write(struct oxygen *chip, unsigned int codec, u8 reg, u8 value) { + struct xonar_pcm179x *data = chip->model_data; + if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == OXYGEN_FUNCTION_SPI) pcm1796_write_spi(chip, codec, reg, value); else pcm1796_write_i2c(chip, codec, reg, value); + if ((unsigned int)(reg - PCM1796_REG_BASE) + < ARRAY_SIZE(data->pcm1796_regs[codec])) + data->pcm1796_regs[codec][reg - PCM1796_REG_BASE] = value; } -static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) +static void pcm1796_write_cached(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) { - oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); + struct xonar_pcm179x *data = chip->model_data; + + if (value != data->pcm1796_regs[codec][reg - PCM1796_REG_BASE]) + pcm1796_write(chip, codec, reg, value); } -static void update_pcm1796_volume(struct oxygen *chip) +static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) { struct xonar_pcm179x *data = chip->model_data; - unsigned int i; - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); - } + oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); + if (reg == CS2000_FUN_CFG_1) + data->cs2000_fun_cfg_1 = value; } -static void update_pcm1796_mute(struct oxygen *chip) +static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value) { struct xonar_pcm179x *data = chip->model_data; - unsigned int i; - u8 value; - value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; - if (chip->dac_mute) - value |= PCM1796_MUTE; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 18, value); + if (reg != CS2000_FUN_CFG_1 || + value != data->cs2000_fun_cfg_1) + cs2000_write(chip, reg, value); } -static void pcm1796_init(struct oxygen *chip) +static void pcm1796_registers_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; for (i = 0; i < data->dacs; ++i) { + /* set ATLD before ATL/ATR */ + pcm1796_write(chip, i, 18, + data->pcm1796_regs[0][18 - PCM1796_REG_BASE]); + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); - pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write(chip, i, 20, + data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 21, 0); } - update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ - update_pcm1796_volume(chip); +} + +static void pcm1796_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | + PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; + pcm1796_registers_init(chip); } static void xonar_d2_init(struct oxygen *chip) @@ -261,7 +280,6 @@ static void xonar_d2_init(struct oxygen *chip) data->generic.anti_pop_delay = 300; data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE; data->dacs = 4; - data->oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -304,7 +322,6 @@ static void xonar_hdav_init(struct oxygen *chip) data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; data->pcm179x.dacs = chip->model.private_data ? 4 : 1; - data->pcm179x.oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -335,7 +352,6 @@ static void xonar_st_init_common(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; - data->oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -438,7 +454,7 @@ static void xonar_st_suspend(struct oxygen *chip) static void xonar_d2_resume(struct oxygen *chip) { - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_enable_output(chip); } @@ -446,14 +462,14 @@ static void xonar_hdav_resume(struct oxygen *chip) { struct xonar_hdav *data = chip->model_data; - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_hdmi_resume(chip, &data->hdmi); xonar_enable_output(chip); } static void xonar_stx_resume(struct oxygen *chip) { - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_enable_output(chip); } @@ -468,11 +484,35 @@ static void set_pcm1796_params(struct oxygen *chip, { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + u8 reg; + + reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < data->dacs; ++i) + pcm1796_write_cached(chip, i, 20, reg); +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + u8 value; - data->oversampling = - params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write_cached(chip, i, 18, value); } static void set_cs2000_params(struct oxygen *chip, @@ -489,9 +529,8 @@ static void set_cs2000_params(struct oxygen *chip, [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, }; - struct xonar_pcm179x *data = chip->model_data; unsigned int rate_index; - u8 rate_mclk; + u8 rate_mclk, reg; rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) & OXYGEN_I2S_RATE_MASK; @@ -499,10 +538,10 @@ static void set_cs2000_params(struct oxygen *chip, oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) - data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + reg = CS2000_REF_CLK_DIV_1; else - data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_2; - cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); + reg = CS2000_REF_CLK_DIV_2; + cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg); } static void set_st_params(struct oxygen *chip, -- cgit v0.10.2 From a361e247b4e36c567b44fef354ab595458422d44 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:19:19 +0200 Subject: sound: virtuoso: add headphone impedance control Add a mixer control to adjust the headphone amplifier output for headphones with different impedances. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index e17ee5e..cf94e44 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -173,6 +173,8 @@ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 pcm1796_regs[4][5]; + bool hp_active; + s8 hp_gain_offset; u8 cs2000_fun_cfg_1; }; @@ -249,13 +251,17 @@ static void pcm1796_registers_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + s8 gain_offset; + gain_offset = data->hp_active ? data->hp_gain_offset : 0; for (i = 0; i < data->dacs; ++i) { /* set ATLD before ATL/ATR */ pcm1796_write(chip, i, 18, data->pcm1796_regs[0][18 - PCM1796_REG_BASE]); - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2] + + gain_offset); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1] + + gain_offset); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); pcm1796_write(chip, i, 20, data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); @@ -352,6 +358,7 @@ static void xonar_st_init_common(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; + data->hp_gain_offset = 2*-18; pcm1796_init(chip); @@ -495,10 +502,14 @@ static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + s8 gain_offset; + gain_offset = data->hp_active ? data->hp_gain_offset : 0; for (i = 0; i < data->dacs; ++i) { - pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]); + pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2] + + gain_offset); + pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1] + + gain_offset); } } @@ -606,6 +617,7 @@ static int st_output_switch_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; u16 gpio_old, gpio; mutex_lock(&chip->mutex); @@ -623,16 +635,83 @@ static int st_output_switch_put(struct snd_kcontrol *ctl, break; } oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + data->hp_active = gpio & GPIO_ST_HP; + update_pcm1796_volume(chip); mutex_unlock(&chip->mutex); return gpio != gpio_old; } -static const struct snd_kcontrol_new st_output_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output", - .info = st_output_switch_info, - .get = st_output_switch_get, - .put = st_output_switch_put, +static int st_hp_volume_offset_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "< 64 ohms", "64-300 ohms", "300-600 ohms" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item > 2) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + mutex_lock(&chip->mutex); + if (data->hp_gain_offset < 2*-6) + value->value.enumerated.item[0] = 0; + else if (data->hp_gain_offset < 0) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + mutex_unlock(&chip->mutex); + return 0; +} + + +static int st_hp_volume_offset_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const s8 offsets[] = { 2*-18, 2*-6, 0 }; + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + s8 offset; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + offset = offsets[value->value.enumerated.item[0]]; + mutex_lock(&chip->mutex); + changed = offset != data->hp_gain_offset; + if (changed) { + data->hp_gain_offset = offset; + update_pcm1796_volume(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new st_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = st_output_switch_get, + .put = st_output_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphones Impedance Playback Enum", + .info = st_hp_volume_offset_info, + .get = st_hp_volume_offset_get, + .put = st_hp_volume_offset_put, + }, }; static void xonar_line_mic_ac97_switch(struct oxygen *chip, @@ -671,7 +750,16 @@ static int xonar_d2_mixer_init(struct oxygen *chip) static int xonar_st_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(st_controls); ++i) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&st_controls[i], chip)); + if (err < 0) + return err; + } + return 0; } static const struct oxygen_model model_xonar_d2 = { -- cgit v0.10.2 From 76ffe1e3fb2f65e98d7ed001c5a2b6f334655364 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:20:11 +0200 Subject: sound: oxygen: allow custom MCLK rates Add a callback that allows model drivers to modify the default I2S MCLK rate. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 2079c10..e3c229b 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -170,6 +170,7 @@ static const struct oxygen_model model_hifier = { .init = hifier_init, .cleanup = hifier_cleanup, .resume = hifier_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_ak4396_params, .set_adc_params = set_cs5340_params, .update_dac_volume = update_ak4396_volume, diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index c986c5e..d12fd9e 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -361,6 +361,7 @@ static const struct oxygen_model model_generic = { .init = generic_init, .cleanup = generic_cleanup, .resume = generic_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_ak4396_params, .set_adc_params = set_wm8785_params, .update_dac_volume = update_ak4396_volume, diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 2ac3b3c..6147216 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -78,6 +78,8 @@ struct oxygen_model { void (*resume)(struct oxygen *chip); void (*pcm_hardware_filter)(unsigned int channel, struct snd_pcm_hardware *hardware); + unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel, + struct snd_pcm_hw_params *hw_params); void (*set_dac_params)(struct oxygen *chip, struct snd_pcm_hw_params *params); void (*set_adc_params)(struct oxygen *chip, @@ -163,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip); /* oxygen_pcm.c */ int oxygen_pcm_init(struct oxygen *chip); +unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel, + struct snd_pcm_hw_params *hw_params); /* oxygen_io.c */ diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 1e98333..9dff695 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -271,13 +271,16 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params) } } -static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params) +unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, + unsigned int channel, + struct snd_pcm_hw_params *hw_params) { if (params_rate(hw_params) <= 96000) return OXYGEN_I2S_MCLK_256; else return OXYGEN_I2S_MCLK_128; } +EXPORT_SYMBOL(oxygen_default_i2s_mclk); static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) { @@ -354,7 +357,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream, OXYGEN_REC_FORMAT_A_MASK); oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, oxygen_rate(hw_params) | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_A, hw_params) | chip->model.adc_i2s_format | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | @@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, if (!is_ac97) oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, oxygen_rate(hw_params) | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_B, + hw_params) | chip->model.adc_i2s_format | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | @@ -472,7 +476,8 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, oxygen_rate(hw_params) | chip->model.dac_i2s_format | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_MULTICH, + hw_params) | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_FORMAT_MASK | diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 330c5e7..a83f827 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -321,6 +321,7 @@ static const struct oxygen_model model_xonar_d1 = { .cleanup = xonar_d1_cleanup, .suspend = xonar_d1_suspend, .resume = xonar_d1_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_cs43xx_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_cs43xx_volume, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index cf94e44..35b3fb4 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -771,6 +771,7 @@ static const struct oxygen_model model_xonar_d2 = { .cleanup = xonar_d2_cleanup, .suspend = xonar_d2_suspend, .resume = xonar_d2_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_pcm1796_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -801,6 +802,7 @@ static const struct oxygen_model model_xonar_hdav = { .suspend = xonar_hdav_suspend, .resume = xonar_hdav_resume, .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_hdav_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -831,6 +833,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, -- cgit v0.10.2 From 973dca93a3d46cca7e4743300f8a510b779906af Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:20:47 +0200 Subject: sound: virtuoso: add PCM1796 oversampling control Add a control to increase the oversampling factor to 128x on cards with PCM1796 or PCM1792A DACs. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 35b3fb4..7f153fb 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -173,8 +173,11 @@ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 pcm1796_regs[4][5]; + unsigned int current_rate; + bool os_128; bool hp_active; s8 hp_gain_offset; + bool has_cs2000; u8 cs2000_fun_cfg_1; }; @@ -277,6 +280,7 @@ static void pcm1796_init(struct oxygen *chip) PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; pcm1796_registers_init(chip); + data->current_rate = 48000; } static void xonar_d2_init(struct oxygen *chip) @@ -401,6 +405,7 @@ static void xonar_st_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + data->has_cs2000 = 1; data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, @@ -486,18 +491,57 @@ static void xonar_st_resume(struct oxygen *chip) xonar_stx_resume(chip); } -static void set_pcm1796_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) +static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate) +{ + struct xonar_pcm179x *data = chip->model_data; + + if (rate <= 32000) + return OXYGEN_I2S_MCLK_512; + else if (rate <= 48000 && data->os_128) + return OXYGEN_I2S_MCLK_512; + else if (rate <= 96000) + return OXYGEN_I2S_MCLK_256; + else + return OXYGEN_I2S_MCLK_128; +} + +static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip, + unsigned int channel, + struct snd_pcm_hw_params *params) +{ + if (channel == PCM_MULTICH) + return mclk_from_rate(chip, params_rate(params)); + else + return oxygen_default_i2s_mclk(chip, channel, params); +} + +static void update_pcm1796_oversampling(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; u8 reg; - reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + if (data->current_rate <= 32000) + reg = PCM1796_OS_128; + else if (data->current_rate <= 48000 && data->os_128) + reg = PCM1796_OS_128; + else if (data->current_rate <= 96000 || data->os_128) + reg = PCM1796_OS_64; + else + reg = PCM1796_OS_32; for (i = 0; i < data->dacs; ++i) pcm1796_write_cached(chip, i, 20, reg); } +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->current_rate = params_rate(params); + update_pcm1796_oversampling(chip); +} + static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; @@ -526,26 +570,44 @@ static void update_pcm1796_mute(struct oxygen *chip) pcm1796_write_cached(chip, i, 18, value); } -static void set_cs2000_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) +static void update_cs2000_rate(struct oxygen *chip, unsigned int rate) { - /* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */ - static const u8 rate_mclks[] = { - [OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, - }; - unsigned int rate_index; + struct xonar_pcm179x *data = chip->model_data; u8 rate_mclk, reg; - rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) - & OXYGEN_I2S_RATE_MASK; - rate_mclk = rate_mclks[rate_index]; + switch (rate) { + /* XXX Why is the I2S A MCLK half the actual I2S MCLK? */ + case 32000: + rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; + break; + case 44100: + if (data->os_128) + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + else + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128; + break; + default: /* 48000 */ + if (data->os_128) + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + else + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128; + break; + case 64000: + rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; + break; + case 88200: + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + break; + case 96000: + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + break; + case 176400: + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + break; + case 192000: + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + break; + } oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) @@ -558,7 +620,7 @@ static void set_cs2000_params(struct oxygen *chip, static void set_st_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { - set_cs2000_params(chip, params); + update_cs2000_rate(chip, params_rate(params)); set_pcm1796_params(chip, params); } @@ -580,6 +642,59 @@ static const struct snd_kcontrol_new alt_switch = { .private_value = GPIO_D2_ALT, }; +static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { "64x", "128x" }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int os_128_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + value->value.enumerated.item[0] = data->os_128; + return 0; +} + +static int os_128_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->os_128; + if (changed) { + data->os_128 = value->value.enumerated.item[0]; + if (data->has_cs2000) + update_cs2000_rate(chip, data->current_rate); + oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, + mclk_from_rate(chip, data->current_rate), + OXYGEN_I2S_MCLK_MASK); + update_pcm1796_oversampling(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new os_128_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Oversampling Playback Enum", + .info = os_128_info, + .get = os_128_get, + .put = os_128_put, +}; + static int st_output_switch_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -745,7 +860,20 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) static int xonar_d2_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; + return 0; +} + +static int xonar_hdav_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); } static int xonar_st_mixer_init(struct oxygen *chip) @@ -759,6 +887,9 @@ static int xonar_st_mixer_init(struct oxygen *chip) if (err < 0) return err; } + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; return 0; } @@ -771,7 +902,7 @@ static const struct oxygen_model model_xonar_d2 = { .cleanup = xonar_d2_cleanup, .suspend = xonar_d2_suspend, .resume = xonar_d2_resume, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_pcm1796_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -798,11 +929,12 @@ static const struct oxygen_model model_xonar_hdav = { .longname = "Asus Virtuoso 200", .chip = "AV200", .init = xonar_hdav_init, + .mixer_init = xonar_hdav_mixer_init, .cleanup = xonar_hdav_cleanup, .suspend = xonar_hdav_suspend, .resume = xonar_hdav_resume, .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_hdav_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -833,7 +965,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, -- cgit v0.10.2 From 4852ad02476ab2bbc874f6f8fda9e677e0f09c87 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:21:21 +0200 Subject: sound: oxygen: add digital filter control Add a control to select between sharp and slow roll-of filter responses of the DACs. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index d12fd9e..3ad9eb0 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -352,6 +352,70 @@ static void set_ak5385_params(struct oxygen *chip, value, GPIO_AK5385_DFS_MASK); } +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Sharp Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + unsigned int i; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->ak4396_regs[0][AK4396_CONTROL_2]; + if (value->value.enumerated.item[0]) + reg |= AK4396_SLOW; + else + reg &= ~AK4396_SLOW; + changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2]; + if (changed) { + for (i = 0; i < 4; ++i) + ak4396_write(chip, i, AK4396_CONTROL_2, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + +static int generic_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); +} + static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static const struct oxygen_model model_generic = { @@ -359,6 +423,7 @@ static const struct oxygen_model model_generic = { .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = generic_init, + .mixer_init = generic_mixer_init, .cleanup = generic_cleanup, .resume = generic_resume, .get_i2s_mclk = oxygen_default_i2s_mclk, diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index a83f827..16c226b 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -69,7 +69,7 @@ struct xonar_cs43xx { struct xonar_generic generic; - u8 cs4398_regs[7]; + u8 cs4398_regs[8]; u8 cs4362a_regs[15]; }; @@ -121,12 +121,11 @@ static void cs43xx_registers_init(struct oxygen *chip) cs4398_write(chip, 4, data->cs4398_regs[4]); cs4398_write(chip, 5, data->cs4398_regs[5]); cs4398_write(chip, 6, data->cs4398_regs[6]); - cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | - CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4398_write(chip, 7, data->cs4398_regs[7]); cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); - cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); + cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]); cs4362a_write(chip, 0x05, 0); for (i = 6; i <= 14; ++i) cs4362a_write(chip, i, data->cs4362a_regs[i]); @@ -147,6 +146,9 @@ static void xonar_d1_init(struct oxygen *chip) CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE; data->cs4398_regs[5] = 60 * 2; data->cs4398_regs[6] = 60 * 2; + data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP | + CS4398_ZERO_CROSS | CS4398_SOFT_RAMP; + data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE; data->cs4362a_regs[6] = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; data->cs4362a_regs[7] = 60 | CS4362A_MUTE; @@ -286,6 +288,68 @@ static const struct snd_kcontrol_new front_panel_switch = { .private_value = GPIO_D1_FRONT_PANEL, }; +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Fast Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_cs43xx *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->cs4398_regs[7] & CS4398_FILT_SEL) != 0; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_cs43xx *data = chip->model_data; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->cs4398_regs[7]; + if (value->value.enumerated.item[0]) + reg |= CS4398_FILT_SEL; + else + reg &= ~CS4398_FILT_SEL; + changed = reg != data->cs4398_regs[7]; + if (changed) { + cs4398_write(chip, 7, reg); + if (reg & CS4398_FILT_SEL) + reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL; + else + reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL; + cs4362a_write(chip, 0x04, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip, unsigned int reg, unsigned int mute) { @@ -309,7 +373,15 @@ static int xonar_d1_control_filter(struct snd_kcontrol_new *template) static int xonar_d1_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); + if (err < 0) + return err; + return 0; } static const struct oxygen_model model_xonar_d1 = { diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 7f153fb..ba18fb5 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -265,7 +265,8 @@ static void pcm1796_registers_init(struct oxygen *chip) + gain_offset); pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1] + gain_offset); - pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 19, + data->pcm1796_regs[0][19 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 20, data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 21, 0); @@ -278,6 +279,8 @@ static void pcm1796_init(struct oxygen *chip) data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + data->pcm1796_regs[0][19 - PCM1796_REG_BASE] = + PCM1796_FLT_SHARP | PCM1796_ATS_1; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; pcm1796_registers_init(chip); data->current_rate = 48000; @@ -642,6 +645,67 @@ static const struct snd_kcontrol_new alt_switch = { .private_value = GPIO_D2_ALT, }; +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Sharp Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->pcm1796_regs[0][19 - PCM1796_REG_BASE] & + PCM1796_FLT_MASK) != PCM1796_FLT_SHARP; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->pcm1796_regs[0][19 - PCM1796_REG_BASE]; + reg &= ~PCM1796_FLT_MASK; + if (!value->value.enumerated.item[0]) + reg |= PCM1796_FLT_SHARP; + else + reg |= PCM1796_FLT_SLOW; + changed = reg != data->pcm1796_regs[0][19 - PCM1796_REG_BASE]; + if (changed) { + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 19, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[2] = { "64x", "128x" }; @@ -858,6 +922,19 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) return 0; } +static int add_pcm1796_controls(struct oxygen *chip) +{ + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; + return 0; +} + static int xonar_d2_mixer_init(struct oxygen *chip) { int err; @@ -865,7 +942,7 @@ static int xonar_d2_mixer_init(struct oxygen *chip) err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); if (err < 0) return err; - err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + err = add_pcm1796_controls(chip); if (err < 0) return err; return 0; @@ -873,7 +950,7 @@ static int xonar_d2_mixer_init(struct oxygen *chip) static int xonar_hdav_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + return add_pcm1796_controls(chip); } static int xonar_st_mixer_init(struct oxygen *chip) @@ -887,7 +964,7 @@ static int xonar_st_mixer_init(struct oxygen *chip) if (err < 0) return err; } - err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + err = add_pcm1796_controls(chip); if (err < 0) return err; return 0; -- cgit v0.10.2 From 1ff048869eb8e8408856e23b3dc6af094491f837 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:21:51 +0200 Subject: sound: oxygen: add high-pass filter control Add a control that allows disabling the high-pass filter of the WM8785 ADC. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 3ad9eb0..acbedeb 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); struct generic_data { u8 ak4396_regs[4][5]; - u16 wm8785_regs[1]; + u16 wm8785_regs[3]; }; static void ak4396_write(struct oxygen *chip, unsigned int codec, @@ -184,6 +184,7 @@ static void wm8785_registers_init(struct oxygen *chip) wm8785_write(chip, WM8785_R7, 0); wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]); + wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); } static void wm8785_init(struct oxygen *chip) @@ -192,6 +193,7 @@ static void wm8785_init(struct oxygen *chip) data->wm8785_regs[0] = WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; + data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL; wm8785_registers_init(chip); snd_component_add(chip->card, "WM8785"); } @@ -334,6 +336,7 @@ static void set_wm8785_params(struct oxygen *chip, if (value != data->wm8785_regs[0]) { wm8785_write(chip, WM8785_R7, 0); wm8785_write(chip, WM8785_R0, value); + wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); } } @@ -411,11 +414,75 @@ static const struct snd_kcontrol_new rolloff_control = { .put = rolloff_put, }; +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "None", "High-pass Filter" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0; + return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + unsigned int reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL); + if (value->value.enumerated.item[0]) + reg |= WM8785_HPFR | WM8785_HPFL; + changed = reg != data->wm8785_regs[WM8785_R2]; + if (changed) + wm8785_write(chip, WM8785_R2, reg); + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new hpf_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Filter Capture Enum", + .info = hpf_info, + .get = hpf_get, + .put = hpf_put, +}; + static int generic_mixer_init(struct oxygen *chip) { return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); } +static int generic_wm8785_mixer_init(struct oxygen *chip) +{ + int err; + + err = generic_mixer_init(chip); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip)); + if (err < 0) + return err; + return 0; +} + static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static const struct oxygen_model model_generic = { @@ -423,7 +490,7 @@ static const struct oxygen_model model_generic = { .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = generic_init, - .mixer_init = generic_mixer_init, + .mixer_init = generic_wm8785_mixer_init, .cleanup = generic_cleanup, .resume = generic_resume, .get_i2s_mclk = oxygen_default_i2s_mclk, @@ -455,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip, switch (id->driver_data) { case MODEL_MERIDIAN: chip->model.init = meridian_init; + chip->model.mixer_init = generic_mixer_init; chip->model.resume = meridian_resume; chip->model.set_adc_params = set_ak5385_params; chip->model.device_config = PLAYBACK_0_TO_I2S | @@ -470,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip, break; case MODEL_CLARO_HALO: chip->model.init = claro_halo_init; + chip->model.mixer_init = generic_mixer_init; chip->model.cleanup = claro_cleanup; chip->model.suspend = claro_suspend; chip->model.resume = claro_resume; -- cgit v0.10.2 From 62428f7b8c873d43be8201e66392c3aad82fec93 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:22:18 +0200 Subject: sound: oxygen: fix input monitor control names Insert "Playback" into the input monitor control names to prevent alsa-lib from treating these controls as global controls. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 5dfb5fb..f375b8a 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -805,7 +805,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -813,7 +813,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, @@ -830,7 +830,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -838,7 +838,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, @@ -855,7 +855,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .index = 1, .info = snd_ctl_boolean_mono_info, .get = monitor_get, @@ -864,7 +864,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .index = 1, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, @@ -882,7 +882,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Monitor Switch", + .name = "Digital Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -890,7 +890,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Monitor Volume", + .name = "Digital Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, -- cgit v0.10.2 From 71623855e20c3febebb5fa60528cde2592678bd5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Sep 2009 13:14:04 +0200 Subject: ALSA: hda - Enable MSI as default Since the recent kernel can handle MSI properly on non-Intel platforms, let's enable MSI as default. If any borken device is found, we can add the quirk entry to the list, which is currently empty. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c9ad182..d0effa3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -60,7 +60,7 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int single_cmd; -static int enable_msi; +static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; #endif @@ -2300,11 +2300,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) } /* - * white-list for enable_msi + * white/black-list for enable_msi */ -static struct snd_pci_quirk msi_white_list[] __devinitdata = { - SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1), - SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1), +static struct snd_pci_quirk msi_black_list[] __devinitdata = { {} }; @@ -2312,10 +2310,12 @@ static void __devinit check_msi(struct azx *chip) { const struct snd_pci_quirk *q; - chip->msi = enable_msi; - if (chip->msi) + if (enable_msi >= 0) { + chip->msi = !!enable_msi; return; - q = snd_pci_quirk_lookup(chip->pci, msi_white_list); + } + chip->msi = 1; /* enable MSI as default */ + q = snd_pci_quirk_lookup(chip->pci, msi_black_list); if (q) { printk(KERN_INFO "hda_intel: msi for device %04x:%04x set to %d\n", -- cgit v0.10.2 From 0afe5f891501609f31146798fb41784f4adad27c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 2 Oct 2009 09:20:00 +0200 Subject: ALSA: hda - Clean up name string creation in patch_realtek.c Use a common helper to create playback controls. This gives less chance of typos. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7810d3d..a751858 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4309,6 +4309,20 @@ static int add_control(struct alc_spec *spec, int type, const char *name, return 0; } +static int add_control_with_pfx(struct alc_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, unsigned long val) +{ + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, val); +} + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val) + #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) @@ -4362,7 +4376,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, 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" }; @@ -4375,26 +4388,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); if (err < 0) @@ -4406,14 +4419,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "Speaker"; else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) @@ -4429,7 +4440,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, { hda_nid_t nid; int err; - char name[32]; if (!pin) return 0; @@ -4443,21 +4453,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, spec->multiout.extra_out_nid[0] = nid; /* control HP volume/switch on the output mixer amp */ nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) return err; } else if (alc880_is_multi_pin(pin)) { /* set manual connection */ /* we have only a switch on HP-out PIN */ - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -4470,16 +4477,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname, int idx, hda_nid_t mix_nid) { - char name[32]; int err; - sprintf(name, "%s Playback Volume", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -5972,7 +5976,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, { hda_nid_t nid_vol; unsigned long vol_val, sw_val; - char name[32]; int err; if (nid >= 0x0f && nid < 0x11) { @@ -5992,14 +5995,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, if (!(*vol_bits & (1 << nid_vol))) { /* first control for the volume widget */ - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val); + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); if (err < 0) return err; *vol_bits |= (1 << nid_vol); } - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val); + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); if (err < 0) return err; return 1; @@ -10936,7 +10937,6 @@ static int alc262_check_volbit(hda_nid_t nid) static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, const char *pfx, int *vbits) { - char name[32]; unsigned long val; int vbit; @@ -10946,28 +10946,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, if (*vbits & vbit) /* a volume control for this mixer already there */ return 0; *vbits |= vbit; - snprintf(name, sizeof(name), "%s Playback Volume", pfx); if (vbit == 2) val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return add_control(spec, ALC_CTL_WIDGET_VOL, name, val); + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val); } static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, const char *pfx) { - char name[32]; unsigned long val; if (!nid) return 0; - snprintf(name, sizeof(name), "%s Playback Switch", pfx); if (nid == 0x16) val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val); + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val); } /* add playback controls from the parsed DAC table */ @@ -12305,11 +12302,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = { static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) { - char name[32]; hda_nid_t dac; int err; - sprintf(name, "%s Playback Volume", ctlname); switch (nid) { case 0x14: case 0x16: @@ -12323,7 +12318,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, } if (spec->multiout.dac_nids[0] != dac && spec->multiout.dac_nids[1] != dac) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(dac, 3, idx, HDA_OUTPUT)); if (err < 0) @@ -12331,12 +12326,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; } - sprintf(name, "%s Playback Switch", ctlname); if (nid != 0x16) - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); else /* mono */ - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); if (err < 0) return err; @@ -12366,8 +12360,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->speaker_pins[0]; if (nid == 0x1d) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Speaker Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); if (err < 0) return err; @@ -12385,8 +12378,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; if (nid == 0x16) { - err = add_control(spec, ALC_CTL_WIDGET_MUTE, - "Mono Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -14235,9 +14227,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec, static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } @@ -15360,7 +15350,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) static int alc861vd_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", "CLFE", "Side"}; hda_nid_t nid_v, nid_s; int i, err; @@ -15377,26 +15366,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, HDA_INPUT)); if (err < 0) @@ -15411,8 +15400,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "PCM"; } else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) @@ -15420,8 +15408,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) pfx = "Speaker"; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) @@ -15439,7 +15426,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, { hda_nid_t nid_v, nid_s; int err; - char name[32]; if (!pin) return 0; @@ -15457,21 +15443,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, nid_s = alc861vd_idx_to_mixer_switch( alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) return err; } else if (alc880_is_multi_pin(pin)) { /* set manual connection */ /* we have only a switch on HP-out PIN */ - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -17213,21 +17196,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec, return 0; } -static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, +static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - sprintf(name, "%s Playback Volume", pfx); - return add_control(spec, ALC_CTL_WIDGET_VOL, name, + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, +static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - sprintf(name, "%s Playback Switch", pfx); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); } @@ -17305,13 +17284,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, return 0; nid = alc662_look_for_dac(codec, pin); if (!nid) { - char name[32]; /* the corresponding DAC is already occupied */ if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) return 0; /* no way */ /* create a switch only */ - sprintf(name, "%s Playback Switch", pfx); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } -- cgit v0.10.2 From b6153e1175a46db9dde17d12609adba7d72330b9 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:23 +0800 Subject: ALSA: HDA VIA: Remove unused IS_VT17xx_VENDORID macro IS_VT17*_VENDORID macros are used nowhere, so clean them up. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ee89db9..9dfe1b5 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -76,14 +76,6 @@ #define VT1702_HP_NID 0x17 #define VT1702_DIGOUT_NID 0x11 -#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) -#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) -#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) -#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) -#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) -#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397) -#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398) - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, -- cgit v0.10.2 From 744ff5f487925223beb6e21460c8cec468b54ab4 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:26 +0800 Subject: ALSA: HDA VIA: Change get_codec_type argument to hda_codec type Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9dfe1b5..e7d739f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -88,8 +88,9 @@ enum VIA_HDA_CODEC { CODEC_TYPES, }; -static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) +static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { + u32 vendor_id = codec->vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -141,7 +142,7 @@ static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); - if (get_codec_type(codec->vendor_id) == VT1708S + if (get_codec_type(codec) == VT1708S && (nid == 0x1a || nid == 0x1e)) { if (size < 4 * sizeof(unsigned int)) return -ENOMEM; @@ -163,7 +164,7 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); - if (get_codec_type(codec->vendor_id) == VT1708S + if (get_codec_type(codec) == VT1708S && (nid == 0x1a || nid == 0x1e)) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; -- cgit v0.10.2 From 518bf3ba753ad93644e7c6cf95c043c918d9429b Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:29 +0800 Subject: ALSA: HDA VIA: Add VT1708B-CE codec support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e7d739f..4d9ffd6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -84,6 +84,7 @@ enum VIA_HDA_CODEC { VT1708B_8CH, VT1708B_4CH, VT1708S, + VT1708BCE, VT1702, CODEC_TYPES, }; @@ -104,9 +105,11 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1709_10CH; else if (dev_id >= 0xe714 && dev_id <= 0xe717) codec_type = VT1709_6CH; - else if (dev_id >= 0xe720 && dev_id <= 0xe723) + else if (dev_id >= 0xe720 && dev_id <= 0xe723) { codec_type = VT1708B_8CH; - else if (dev_id >= 0xe724 && dev_id <= 0xe727) + if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) + codec_type = VT1708BCE; + } else if (dev_id >= 0xe724 && dev_id <= 0xe727) codec_type = VT1708B_4CH; else if ((dev_id & 0xfff) == 0x397 && (dev_id >> 12) < 8) @@ -224,6 +227,8 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; + enum VIA_HDA_CODEC codec_type; + #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -979,6 +984,10 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + spec->codec_type = get_codec_type(codec); + if (spec->codec_type == VT1708BCE) + spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost + same */ /* Lydia Add for EAPD enable */ if (!spec->dig_in_nid) { /* No Digital In connection */ if (spec->dig_in_pin) { @@ -2369,12 +2378,14 @@ static struct hda_amp_list vt1708B_loopbacks[] = { { } /* end */ }; #endif - +static int patch_vt1708S(struct hda_codec *codec); static int patch_vt1708B_8ch(struct hda_codec *codec) { struct via_spec *spec; int err; + if (get_codec_type(codec) == VT1708BCE) + return patch_vt1708S(codec); /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -2906,6 +2917,16 @@ static int patch_vt1708S(struct hda_codec *codec) spec->loopback.amplist = vt1708S_loopbacks; #endif + /* correct names for VT1708BCE */ + if (get_codec_type(codec) == VT1708BCE) { + kfree(codec->chip_name); + codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); + spec->stream_name_analog = "VT1708BCE Analog"; + spec->stream_name_digital = "VT1708BCE Digital"; + } return 0; } -- cgit v0.10.2 From c2c02ea326d3683f551120e74a297b354a223357 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:32 +0800 Subject: ALSA: HDA VIA: Limit VT1702 AA-Path max volume according to customer request, VT1702 AA-Path max volume (12 dB) is too high, so limit to 0 dB. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4d9ffd6..e626989 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -3166,6 +3166,12 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; + /* limit AA path volume to 0 dB */ + snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) return err; -- cgit v0.10.2 From f5271101faf1655d862849f42518c2a88ef394fb Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:35 +0800 Subject: ALSA HDA VIA: Add VIA_CTL_WIDGET_ANALOG_MUTE control type Enter low power state if AA-Path volume is muted. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e626989..d6bee62 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -128,6 +128,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, + VIA_CTL_WIDGET_ANALOG_MUTE, }; enum { @@ -177,9 +178,34 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, return 0; } +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); +static void set_jack_power_state(struct hda_codec *codec); + +static int analog_input_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + set_jack_power_state(codec); + analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + return change; +} + +/* modify .put = snd_hda_mixer_amp_switch_put */ +#define ANALOG_INPUT_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = analog_input_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + static struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + ANALOG_INPUT_MUTE, }; @@ -303,7 +329,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -362,6 +388,131 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } + +static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int *affected_parm) +{ + unsigned parm; + unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); + unsigned no_presence = (def_conf & AC_DEFCFG_MISC) + >> AC_DEFCFG_MISC_SHIFT + & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ + unsigned present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31; + + if ((no_presence || present) && get_defcfg_connect(def_conf) + != AC_JACK_PORT_NONE) { + *affected_parm = AC_PWRST_D0; /* if it's connected */ + parm = AC_PWRST_D0; + } else + parm = AC_PWRST_D3; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); +} + +static void set_jack_power_state(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int imux_is_smixer; + unsigned int parm; + + if (spec->codec_type == VT1702) { + imux_is_smixer = snd_hda_codec_read( + codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 1/2/5 (14h/15h/18h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x14, &parm); + set_pin_power_state(codec, 0x15, &parm); + set_pin_power_state(codec, 0x18, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ + /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW 3/4 (16h/17h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x16, &parm); + set_pin_power_state(codec, 0x17, &parm); + /* MW0 (1ah), AOW 0/1 (10h/1dh) */ + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, + parm); + } else if (spec->codec_type == VT1708B_8CH + || spec->codec_type == VT1708B_4CH + || spec->codec_type == VT1708S) { + /* SW0 (17h) = stereo mixer */ + int is_8ch = spec->codec_type != VT1708B_4CH; + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) + == ((spec->codec_type == VT1708S) ? 5 : 0); + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW 0/1 (13h/14h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW6 (22h), SW2 (26h), AOW2 (24h) */ + if (is_8ch) { + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x22, &parm); + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x24, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* PW 3/4/7 (1ch/1dh/23h) */ + parm = AC_PWRST_D3; + /* force to D0 for internal Speaker */ + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + if (is_8ch) + set_pin_power_state(codec, 0x23, &parm); + /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + if (is_8ch) { + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x27, 0, + AC_VERB_SET_POWER_STATE, parm); + } + } +} + /* * input MUX handling */ @@ -504,6 +655,93 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { }, { } /* end */ }; + +/* check AA path's mute statue */ +static int is_aa_path_mute(struct hda_codec *codec) +{ + int mute = 1; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + struct via_spec *spec = codec->spec; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + case VT1702: + nid_mixer = 0x1a; + start_idx = 1; + end_idx = 3; + break; + default: + return 0; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + unsigned int con_list = snd_hda_codec_read( + codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); + int shift = 8 * (i % 4); + hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; + unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); + if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { + /* check mute status while the pin is connected */ + int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, + HDA_INPUT, i) >> 7; + int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, + HDA_INPUT, i) >> 7; + if (!mute_l || !mute_r) { + mute = 0; + break; + } + } + } + return mute; +} + +/* enter/exit analog low-current mode */ +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +{ + struct via_spec *spec = codec->spec; + static int saved_stream_idle = 1; /* saved stream idle status */ + int enable = is_aa_path_mute(codec); + unsigned int verb = 0; + unsigned int parm = 0; + + if (stream_idle == -1) /* stream status did not change */ + enable = enable && saved_stream_idle; + else { + enable = enable && stream_idle; + saved_stream_idle = stream_idle; + } + + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + default: + return; /* other codecs are not supported */ + } + /* send verb */ + snd_hda_codec_write(codec, codec->afg, 0, verb, parm); +} + /* * generic initialization of ADC, input mixers and output mixers */ -- cgit v0.10.2 From 173143791068ac9f155c378a591d0b3d6c4a45ca Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:37 +0800 Subject: ALSA: HDA VIA: Add low current mode for power saving. For VT1708B, VT1708S and VT1702, enter low current mode if no analog stream is opened and all aa path mute. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d6bee62..7ace0fc 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -783,6 +783,10 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); } @@ -1089,6 +1093,11 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* init power states */ + set_jack_power_state(codec); + analog_low_current_mode(codec, 1); + via_free_kctls(codec); /* no longer needed */ return 0; } @@ -2312,6 +2321,17 @@ static struct hda_verb vt1708B_uniwill_init_verbs[] = { { } }; +static int via_pcm_open_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); + return 0; +} + static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .substreams = 2, .channels_min = 2, @@ -2320,7 +2340,8 @@ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2342,8 +2363,10 @@ static struct hda_pcm_stream vt1708B_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2800,7 +2823,8 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .cleanup = via_playback_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2810,8 +2834,10 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -3236,7 +3262,8 @@ static struct hda_pcm_stream vt1702_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -3246,8 +3273,10 @@ static struct hda_pcm_stream vt1702_pcm_analog_capture = { .channels_max = 2, .nid = 0x12, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; -- cgit v0.10.2 From 9510e8dd9cb4469d146953270364af6dd86a39be Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:39 +0800 Subject: ALSA: HDA VIA: Remove unused argument of via_new_analog_input Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 7ace0fc..0da57db 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -317,8 +317,8 @@ static void via_free_kctls(struct hda_codec *codec) } /* create input playback/capture controls for the given pin */ -static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, - const char *ctlname, int idx, int mix_nid) +static int via_new_analog_input(struct via_spec *spec, const char *ctlname, + int idx, int mix_nid) { char name[32]; int err; @@ -1480,8 +1480,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x17); + err = via_new_analog_input(spec, labels[i], idx, 0x17); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2014,8 +2013,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x18); + err = via_new_analog_input(spec, labels[i], idx, 0x18); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2576,8 +2574,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -3048,8 +3045,7 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -3402,8 +3398,7 @@ static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, idx = 3; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], - labels[i], idx, 0x1A); + err = via_new_analog_input(spec, labels[i], idx, 0x1A); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; -- cgit v0.10.2 From 0713efebfa1a1878feeeb17cbadc3d2d2c9e9ed2 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:43 +0800 Subject: ALSA: HDA VIA: Change VT1708S & VT1702 hp mode controls For VT1708S and VT1702, deactivate "Headphone Playback Volume" and "Headphone Playback Mute" control if "Independent HP" mode is OFF. and rename VT1702 "Independent HP" text. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0da57db..9e8dd57 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -572,6 +572,18 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, return 0; } +static void activate_ctl(struct hda_codec *codec, const char *name, int active) +{ + struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); + if (ctl) { + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access |= active + ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + } +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -620,6 +632,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, spec->multiout.hp_nid, 0, 0, 0); + /* update HP volume/swtich active state */ + if (spec->codec_type == VT1708S + || spec->codec_type == VT1702) { + activate_ctl(codec, "Headphone Playback Volume", + spec->hp_independent_mode); + activate_ctl(codec, "Headphone Playback Switch", + spec->hp_independent_mode); + } return 0; } @@ -3342,11 +3362,11 @@ static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) { - int err; - + int err, i; + struct hda_input_mux *imux; + static const char *texts[] = { "ON", "OFF", NULL}; if (!pin) return 0; - spec->multiout.hp_nid = 0x1D; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, @@ -3361,8 +3381,18 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) if (err < 0) return err; - create_hp_imux(spec); + imux = &spec->private_imux[1]; + /* for hp mode select */ + i = 0; + while (texts[i] != NULL) { + imux->items[imux->num_items].label = texts[i]; + imux->items[imux->num_items].index = i; + imux->num_items++; + i++; + } + + spec->hp_mux = &spec->private_imux[1]; return 0; } -- cgit v0.10.2 From cdc1784d49258198df600fbc1d37c07d7eee5ed6 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:47 +0800 Subject: ALSA: HDA VIA: Rewrite via_independent_hp_put Use hp_independent_mode_index to store hp index, and simplify function via_independent_hp_put with it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9e8dd57..e3bd526 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -252,6 +252,7 @@ struct via_spec { /* HP mode source */ const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; + unsigned int hp_independent_mode_index; enum VIA_HDA_CODEC codec_type; @@ -584,6 +585,36 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active) } } +static int update_side_mute_status(struct hda_codec *codec) +{ + /* mute side channel */ + struct via_spec *spec = codec->spec; + unsigned int parm = spec->hp_independent_mode + ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; + hda_nid_t sw3; + + switch (spec->codec_type) { + case VT1708: + sw3 = 0x1b; + break; + case VT1709_10CH: + sw3 = 0x29; + break; + case VT1708B_8CH: + case VT1708S: + sw3 = 0x27; + break; + default: + sw3 = 0; + break; + } + + if (sw3) + snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm); + return 0; +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -591,47 +622,18 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct via_spec *spec = codec->spec; hda_nid_t nid = spec->autocfg.hp_pins[0]; unsigned int pinsel = ucontrol->value.enumerated.item[0]; - unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - - if (con_nid == spec->multiout.hp_nid) { - if (pinsel == 0) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } else if (pinsel == 1) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } - } else { - if (pinsel == 0) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } else if (pinsel == 1) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - pinsel); + /* Get Independent Mode index of headphone pin widget */ + spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel + ? 1 : 0; - if (spec->multiout.hp_nid && - spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, - spec->multiout.hp_nid, - 0, 0, 0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + + if (spec->multiout.hp_nid && spec->multiout.hp_nid + != spec->multiout.dac_nids[HDA_FRONT]) + snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, + 0, 0, 0); + update_side_mute_status(codec); /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S || spec->codec_type == VT1702) { @@ -1447,6 +1449,7 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -1982,6 +1985,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) spec->multiout.hp_nid = VT1709_HP_DAC_NID; else if (spec->multiout.num_dacs == 3) /* 6 channels */ spec->multiout.hp_nid = 0; + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -2541,6 +2545,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -3011,6 +3016,7 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -3368,6 +3374,7 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) if (!pin) return 0; spec->multiout.hp_nid = 0x1D; + spec->hp_independent_mode_index = 0; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", -- cgit v0.10.2 From 1564b2878f5cf160f60af99d4dbca1dd7809ee8a Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:52 +0800 Subject: ALSA: HDA VIA: Add smart5.1 function. Smart 5.1 is for 3-jacks model, to reuse input pins as outputs. While off, they act as "line out" / "line in" / "mic in". While on, they acts as "line out" / "back left/right" / "center/lfe". Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e3bd526..26ee1c3 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -211,7 +211,7 @@ static struct snd_kcontrol_new vt1708_control_templates[] = { struct via_spec { /* codec parameterization */ - struct snd_kcontrol_new *mixers[3]; + struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; struct hda_verb *init_verbs[5]; @@ -253,6 +253,7 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; + unsigned int smart51_enabled; enum VIA_HDA_CODEC codec_type; @@ -390,6 +391,8 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); + static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -400,9 +403,10 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ unsigned present = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) >> 31; - - if ((no_presence || present) && get_defcfg_connect(def_conf) - != AC_JACK_PORT_NONE) { + struct via_spec *spec = codec->spec; + if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + || ((no_presence || present) + && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ parm = AC_PWRST_D0; } else @@ -657,6 +661,167 @@ static struct snd_kcontrol_new via_hp_mixer[] = { { } /* end */ }; +static void notify_aa_path_ctls(struct hda_codec *codec) +{ + int i; + struct snd_ctl_elem_id id; + const char *labels[] = {"Mic", "Front Mic", "Line"}; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + for (i = 0; i < ARRAY_SIZE(labels); i++) { + sprintf(id.name, "%s Playback Volume", labels[i]); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +static void mute_aa_path(struct hda_codec *codec, int mute) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708: + nid_mixer = 0x17; + start_idx = 2; + end_idx = 4; + break; + case VT1709_10CH: + case VT1709_6CH: + nid_mixer = 0x18; + start_idx = 2; + end_idx = 4; + break; + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + default: + return; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; + snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, + HDA_AMP_MUTE, val); + } +} +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) +{ + int res = 0; + int index; + for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { + if (pin == spec->autocfg.input_pins[index]) { + res = 1; + break; + } + } + return res; +} + +static int via_smart51_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *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 via_smart51_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int on = 1; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (nid) { + int ctl = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0); + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode) + continue; /* ignore FMic for independent HP */ + if (ctl & AC_PINCTL_IN_EN + && !(ctl & AC_PINCTL_OUT_EN)) + on = 0; + } + } + *ucontrol->value.integer.value = on; + return 0; +} + +static int via_smart51_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int out_in = *ucontrol->value.integer.value + ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode) + continue; /* don't retask FMic for independent HP */ + if (nid) { + unsigned int parm = snd_hda_codec_read( + codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + } + if (i == AUTO_PIN_FRONT_MIC) { + if (spec->codec_type == VT1708S) { + /* input = index 1 (AOW3) */ + snd_hda_codec_write( + codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, 1); + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, + 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); + } + } + } + spec->smart51_enabled = *ucontrol->value.integer.value; + set_jack_power_state(codec); + return 1; +} + +static struct snd_kcontrol_new via_smart51_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = via_smart51_info, + .get = via_smart51_get, + .put = via_smart51_put, + }, + {} /* end */ +}; + /* capture mixer elements */ static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -1587,6 +1752,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2087,6 +2253,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2649,6 +2816,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -3142,6 +3310,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } -- cgit v0.10.2 From a80e6e3c8c21ca50837e2e42fa438a4ff4a9788e Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:55 +0800 Subject: ALSA: HDA VIA: When changing input source, update power state. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 26ee1c3..c5e9994 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -549,6 +549,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, if (!spec->mux_nids[adc_idx]) return -EINVAL; + /* switch to D0 beofre change index */ + if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + /* update jack power state */ + set_jack_power_state(codec); + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); -- cgit v0.10.2 From a34df19a658170fb7125e8017ee46ba54b1ad495 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:01 +0800 Subject: ALSA: HDA VIA: Add VIA_JACK_EVENT process in via_unsol_event. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c5e9994..cd62c88 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -124,6 +124,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 +#define VIA_JACK_EVENT 0x04 enum { VIA_CTL_WIDGET_VOL, @@ -1413,10 +1414,12 @@ static void via_unsol_event(struct hda_codec *codec, unsigned int res) { res >>= 26; - if (res == VIA_HP_EVENT) + if (res & VIA_HP_EVENT) via_hp_automute(codec); - else if (res == VIA_GPIO_EVENT) + if (res & VIA_GPIO_EVENT) via_gpio_control(codec); + if (res & VIA_JACK_EVENT) + set_jack_power_state(codec); } static int via_init(struct hda_codec *codec) @@ -1878,7 +1881,8 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = { }; static struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, { } }; @@ -2514,7 +2518,15 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { }; static struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -3009,7 +3021,15 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { }; static struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -3448,8 +3468,12 @@ static struct hda_verb vt1702_volume_init_verbs[] = { }; static struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; -- cgit v0.10.2 From dcf34c8cc685781cebbe1f4c75272a3269eba3a1 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:15 +0800 Subject: ALSA: HDA VIA: Refresh front playback mute in via_hp_automute. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index cd62c88..c1f4307 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1351,14 +1351,25 @@ static void via_free(struct hda_codec *codec) /* mute internal speaker if HP is plugged */ static void via_hp_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = 0; struct via_spec *spec = codec->spec; present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - present ? HDA_AMP_MUTE : 0); + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + /* auto mute */ + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Front Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } } static void via_gpio_control(struct hda_codec *codec) -- cgit v0.10.2 From 1f2e99febd5dd0c91f0d0752674029a4376649e5 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:17 +0800 Subject: ALSA: HDA VIA: Add Jack detect feature for VT1708. VT1708 does not support unsolicited response, but we need hp detect to automute speaker. Implemented in workqueue. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c1f4307..38418a5 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -89,6 +89,64 @@ enum VIA_HDA_CODEC { CODEC_TYPES, }; +struct via_spec { + /* codec parameterization */ + struct snd_kcontrol_new *mixers[4]; + unsigned int num_mixers; + + struct hda_verb *init_verbs[5]; + unsigned int num_iverbs; + + char *stream_name_analog; + struct hda_pcm_stream *stream_analog_playback; + struct hda_pcm_stream *stream_analog_capture; + + char *stream_name_digital; + struct hda_pcm_stream *stream_digital_playback; + struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + hda_nid_t slave_dig_outs[2]; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t mux_nids[3]; + hda_nid_t dig_in_nid; + hda_nid_t dig_in_pin; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + + /* PCM information */ + struct hda_pcm pcm_rec[3]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_imux[2]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + + /* HP mode source */ + const struct hda_input_mux *hp_mux; + unsigned int hp_independent_mode; + unsigned int hp_independent_mode_index; + unsigned int smart51_enabled; + + enum VIA_HDA_CODEC codec_type; + + /* work to check hp jack state */ + struct hda_codec *codec; + struct delayed_work vt1708_hp_work; + int vt1708_jack_detectect; + int vt1708_hp_present; +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif +}; + static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { u32 vendor_id = codec->vendor_id; @@ -181,6 +239,31 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); static void set_jack_power_state(struct hda_codec *codec); +static int is_aa_path_mute(struct hda_codec *codec); + +static void vt1708_start_hp_work(struct via_spec *spec) +{ + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + if (!delayed_work_pending(&spec->vt1708_hp_work)) + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); +} + +static void vt1708_stop_hp_work(struct via_spec *spec) +{ + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 + && !is_aa_path_mute(spec->codec)) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + cancel_delayed_work(&spec->vt1708_hp_work); + flush_scheduled_work(); +} static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -190,6 +273,12 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, set_jack_power_state(codec); analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { + if (is_aa_path_mute(codec)) + vt1708_start_hp_work(codec->spec); + else + vt1708_stop_hp_work(codec->spec); + } return change; } @@ -210,59 +299,6 @@ static struct snd_kcontrol_new vt1708_control_templates[] = { }; -struct via_spec { - /* codec parameterization */ - struct snd_kcontrol_new *mixers[4]; - unsigned int num_mixers; - - struct hda_verb *init_verbs[5]; - unsigned int num_iverbs; - - char *stream_name_analog; - struct hda_pcm_stream *stream_analog_playback; - struct hda_pcm_stream *stream_analog_capture; - - char *stream_name_digital; - struct hda_pcm_stream *stream_digital_playback; - struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; - hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; - - /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux[2]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - - /* HP mode source */ - const struct hda_input_mux *hp_mux; - unsigned int hp_independent_mode; - unsigned int hp_independent_mode_index; - unsigned int smart51_enabled; - - enum VIA_HDA_CODEC codec_type; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; -#endif -}; - static hda_nid_t vt1708_adc_nids[2] = { /* ADC1-2 */ 0x15, 0x27 @@ -981,7 +1017,6 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct via_spec *spec = codec->spec; int idle = substream->pstr->substream_opened == 1 && substream->ref_count == 0; - analog_low_current_mode(codec, idle); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); @@ -994,6 +1029,7 @@ static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + vt1708_start_hp_work(spec); return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); } @@ -1003,6 +1039,7 @@ static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } @@ -1094,7 +1131,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); } - + vt1708_start_hp_work(spec); return 0; } @@ -1134,7 +1171,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); } - + vt1708_stop_hp_work(spec); return 0; } @@ -1345,6 +1382,7 @@ static void via_free(struct hda_codec *codec) return; via_free_kctls(codec); + vt1708_stop_hp_work(spec); kfree(codec->spec); } @@ -1464,6 +1502,15 @@ static int via_init(struct hda_codec *codec) return 0; } +#ifdef SND_HDA_NEEDS_RESUME +static int via_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); + return 0; +} +#endif + #ifdef CONFIG_SND_HDA_POWER_SAVE static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -1479,6 +1526,9 @@ static struct hda_codec_ops via_patch_ops = { .build_pcms = via_build_pcms, .init = via_init, .free = via_free, +#ifdef SND_HDA_NEEDS_RESUME + .suspend = via_suspend, +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = via_check_power_status, #endif @@ -1728,6 +1778,51 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) return; } +static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = + !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); + ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + return 0; +} + +static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int change; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; + change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) + == !spec->vt1708_jack_detectect; + if (spec->vt1708_jack_detectect) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + return change; +} + +static struct snd_kcontrol_new vt1708_jack_detectect[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detectect_get, + .put = vt1708_jack_detectect_put, + }, + {} /* end */ +}; + static int vt1708_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1755,6 +1850,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) return err; + /* add jack detect on/off control */ + err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); + if (err < 0) + return err; spec->multiout.max_channels = spec->multiout.num_dacs * 2; @@ -1788,6 +1887,22 @@ static int via_auto_init(struct hda_codec *codec) return 0; } +static void vt1708_update_hp_jack_state(struct work_struct *work) +{ + struct via_spec *spec = container_of(work, struct via_spec, + vt1708_hp_work.work); + if (spec->codec_type != VT1708) + return; + /* if jack state toggled */ + if (spec->vt1708_hp_present + != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31)) { + spec->vt1708_hp_present ^= 1; + via_hp_automute(spec->codec); + } + vt1708_start_hp_work(spec); +} + static int get_mux_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1864,7 +1979,8 @@ static int patch_vt1708(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif - + spec->codec = codec; + INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } -- cgit v0.10.2 From 82ef9e45c48634af5e3f6ab9ac75b6642c538020 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:19 +0800 Subject: ALSA: HDA VIA: Modify vt1708_set_pinconfig_connect function. like seqassoc 0xff, seqassoc 0xf0 of vt1708 should override Port Connectivity field into 'AC_JACK_PORT_COMPLEX' Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 38418a5..dc416ec 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1768,11 +1768,10 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) def_conf = snd_hda_codec_get_pincfg(codec, nid); seqassoc = (unsigned char) get_defcfg_association(def_conf); seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { - if (seqassoc == 0xff) { - def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); - snd_hda_codec_set_pincfg(codec, nid, def_conf); - } + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE + && (seqassoc == 0xf0 || seqassoc == 0xff)) { + def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); + snd_hda_codec_set_pincfg(codec, nid, def_conf); } return; -- cgit v0.10.2 From c873cc25280113d71463ad5075413d283be6b766 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:21 +0800 Subject: ALSA: HDA VIA: Replace via_playback_pcm_prepare/cleanup Replaced with via_playback_multi_pcm_prepare/cleanup to support multi-stream operations Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index dc416ec..4d3c447 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1022,28 +1022,6 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, hinfo); } -static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - vt1708_start_hp_work(spec); - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - - static void playback_multi_pcm_prep_0(struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -1252,7 +1230,7 @@ static struct hda_pcm_stream vt1708_pcm_analog_playback = { }; static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 1, + .substreams = 2, .channels_min = 2, .channels_max = 8, .nid = 0x10, /* NID to query formats and rates */ @@ -1263,8 +1241,8 @@ static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup }, }; @@ -2062,8 +2040,8 @@ static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -2074,8 +2052,8 @@ static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -3166,8 +3144,8 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, .close = via_pcm_open_close }, }; -- cgit v0.10.2 From 9645c2039d5cfdbdcebe297420e180b6cd262836 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:27 +0800 Subject: ALSA: HDA VIA: Modify vt1708_auto_create_multi_out_ctls. Rewrite nid_vol/mute assignment for clearity, and check line connection before adding control for it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4d3c447..efadacd 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1553,7 +1553,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid, nid_vol = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { @@ -1562,8 +1562,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; - if (i != AUTO_SEQ_FRONT) - nid_vol = 0x18 + i; + nid_vol = nid_vols[i]; if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ @@ -1595,13 +1594,13 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; -- cgit v0.10.2 From 4483a2f5907fa824bd6384c36fdcee9777cab1b9 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:29 +0800 Subject: ALSA: HDA VIA: Modify vt1709_auto_create_multi_out_ctls. Rewrite nid_vol/mute assignment for clearity, and check line connection before adding control for it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index efadacd..f9702a1 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2160,7 +2160,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { @@ -2169,43 +2169,45 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; + nid_vol = nid_vols[i]; + if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ - /* add control to mixer index 0 */ + /* ADD control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; @@ -2226,26 +2228,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, } else if (i == AUTO_SEQ_SURROUND) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SIDE) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; -- cgit v0.10.2 From 6369bcfccb57da28ad3e09b25fecd841a415ae95 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:31 +0800 Subject: ALSA: HDA VIA: Replace MIC_BOOST_VOLUME. With snd_hda_override_amp_caps. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f9702a1..4b7cd59 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -197,46 +197,6 @@ enum { AUTO_SEQ_SIDE }; -/* Some VT1708S based boards gets the micboost setting wrong, so we have - * to apply some brute-force and re-write the TLV's by software. */ -static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - - if (get_codec_type(codec) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; - if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */ - return -EFAULT; - if (put_user(2 * sizeof(unsigned int), _tlv + 1)) - return -EFAULT; - if (put_user(0, _tlv + 2)) /* offset = 0 */ - return -EFAULT; - if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ - return -EFAULT; - } - return 0; -} - -static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - - if (get_codec_type(codec) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - } - return 0; -} - static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); static void set_jack_power_state(struct hda_codec *codec); static int is_aa_path_mute(struct hda_codec *codec); @@ -3063,29 +3023,15 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) /* Patch for VT1708S */ -/* VT1708S software backdoor based override for buggy hardware micboost - * setting */ -#define MIC_BOOST_VOLUME(xname, nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = mic_boost_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = snd_hda_mixer_amp_volume_put, \ - .tlv = { .c = mic_boost_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } - /* capture mixer elements */ static struct snd_kcontrol_new vt1708S_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), - MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer @@ -3457,6 +3403,16 @@ static struct hda_amp_list vt1708S_loopbacks[] = { }; #endif +static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, + int offset, int num_steps, int step_size) +{ + snd_hda_override_amp_caps(codec, pin, HDA_INPUT, + (offset << AC_AMPCAP_OFFSET_SHIFT) | + (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | + (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); +} + static int patch_vt1708S(struct hda_codec *codec) { struct via_spec *spec; @@ -3493,6 +3449,8 @@ static int patch_vt1708S(struct hda_codec *codec) spec->adc_nids = vt1708S_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; spec->num_mixers++; } -- cgit v0.10.2 From bc7e7e5ce05047e16633a94d36fa144af1d2b4c7 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:32 +0800 Subject: ALSA: HDA VIA: Move backdoor verbs to vt17xx_volume_init_verb As init verbs, vt17xx_volume_init_verb is a better place to hold them. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4b7cd59..1c87231 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -3068,6 +3068,8 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, { } }; @@ -3527,6 +3529,10 @@ static struct hda_verb vt1702_volume_init_verbs[] = { /* PW6 PW7 Output enable */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* mixer enable */ + {0x1, 0xF88, 0x3}, + /* GPIO 0~2 */ + {0x1, 0xF82, 0x3F}, { } }; @@ -3768,8 +3774,6 @@ static int patch_vt1702(struct hda_codec *codec) { struct via_spec *spec; int err; - unsigned int response; - unsigned char control; /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -3814,18 +3818,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->loopback.amplist = vt1702_loopbacks; #endif - /* Open backdoor */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); - control = (unsigned char)(response & 0xff); - control |= 0x3; - snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); - - /* Enable GPIO 0&1 for volume&mute control */ - /* Enable GPIO 2 for DMIC-DATA */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); - control = (unsigned char)((response >> 16) & 0x3f); - snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); - return 0; } -- cgit v0.10.2 From eb7188cafcb7aa1419b8889494cdbd4e6a01da1c Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:34 +0800 Subject: ALSA: HDA VIA: Add VT1718S support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 1c87231..c783853 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -86,6 +86,7 @@ enum VIA_HDA_CODEC { VT1708S, VT1708BCE, VT1702, + VT1718S, CODEC_TYPES, }; @@ -175,6 +176,9 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x398 && (dev_id >> 12) < 8) codec_type = VT1702; + else if ((dev_id & 0xfff) == 0x428 + && (dev_id >> 12) < 8) + codec_type = VT1718S; else codec_type = UNKNOWN; return codec_type; @@ -284,6 +288,11 @@ static hda_nid_t vt1702_adc_nids[3] = { 0x12, 0x20, 0x1F }; +static hda_nid_t vt1718S_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -512,6 +521,67 @@ static void set_jack_power_state(struct hda_codec *codec) snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); } + } else if (spec->codec_type == VT1718S) { + /* MUX6 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x27, &parm); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW2 (26h), AOW2 (ah) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW0/1 (24h/25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + set_pin_power_state(codec, 0x25, &parm); + if (!spec->hp_independent_mode) /* check for redirected HP */ + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, + parm); + /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ + snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + if (spec->hp_independent_mode) { + /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0xc, 0, + AC_VERB_SET_POWER_STATE, parm); + } } } @@ -572,11 +642,21 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.hp_pins[0]; - unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); + hda_nid_t nid; + unsigned int pinsel; + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + /* use !! to translate conn sel 2 for VT1718S */ + pinsel = !!snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, + 0x00); ucontrol->value.enumerated.item[0] = pinsel; return 0; @@ -635,6 +715,16 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ + spec->multiout.num_dacs = 4; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid @@ -645,7 +735,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, update_side_mute_status(codec); /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S - || spec->codec_type == VT1702) { + || spec->codec_type == VT1702 + || spec->codec_type == VT1718S) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -758,7 +849,8 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode) + && spec->hp_independent_mode + && spec->codec_type != VT1718S) continue; /* ignore FMic for independent HP */ if (ctl & AC_PINCTL_IN_EN && !(ctl & AC_PINCTL_OUT_EN)) @@ -782,7 +874,8 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, for (i = 0; i < ARRAY_SIZE(index); i++) { hda_nid_t nid = spec->autocfg.input_pins[index[i]]; if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode) + && spec->hp_independent_mode + && spec->codec_type != VT1718S) continue; /* don't retask FMic for independent HP */ if (nid) { unsigned int parm = snd_hda_codec_read( @@ -797,6 +890,10 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } + if (spec->codec_type == VT1718S) + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, + HDA_AMP_UNMUTE); } if (i == AUTO_PIN_FRONT_MIC) { if (spec->codec_type == VT1708S) { @@ -871,6 +968,11 @@ static int is_aa_path_mute(struct hda_codec *codec) start_idx = 1; end_idx = 3; break; + case VT1718S: + nid_mixer = 0x21; + start_idx = 1; + end_idx = 3; + break; default: return 0; } @@ -920,6 +1022,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ break; case VT1708S: + case VT1718S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; @@ -1026,8 +1129,8 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - !spec->hp_independent_mode) + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] + && !spec->hp_independent_mode) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); @@ -3821,6 +3924,435 @@ static int patch_vt1702(struct hda_codec *codec) return 0; } +/* Patch for VT1718S */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1718S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1718S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + + /* Setup default input of Front HP to MW9 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* PW9 PW10 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + /* PW11 Input enable */ + {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf88, 0x8}, + /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Unmute MW4's index 0 */ + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { } +}; + + +static struct hda_verb vt1718S_uniwill_init_verbs[] = { + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 10, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 4; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x8; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0xa; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x9; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0xb; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; + hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; + hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; + hda_nid_t nid, nid_vol, nid_mute = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + /* Center/LFE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + /* Front */ + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0xc; /* AOW4 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 1; + break; + + case 0x2a: /* Line In */ + idx = 2; + break; + + case 0x29: /* Front Mic */ + idx = 3; + break; + + case 0x2c: /* CD */ + idx = 0; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + return 0; +} + +static int vt1718S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + + if (err < 0) + return err; + err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) + spec->dig_in_nid = 0x13; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1718S_loopbacks[] = { + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { 0x21, HDA_INPUT, 3 }, + { 0x21, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1718S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1718S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; + + spec->stream_name_analog = "VT1718S Analog"; + spec->stream_analog_playback = &vt1718S_pcm_analog_playback; + spec->stream_analog_capture = &vt1718S_pcm_analog_capture; + + spec->stream_name_digital = "VT1718S Digital"; + spec->stream_digital_playback = &vt1718S_pcm_digital_playback; + if (codec->vendor_id == 0x11060428) + spec->stream_digital_capture = &vt1718S_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1718S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1718S_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -3893,6 +4425,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1702}, { .id = 0x11067398, .name = "VT1702", .patch = patch_vt1702}, + { .id = 0x11060428, .name = "VT1718S", + .patch = patch_vt1718S}, + { .id = 0x11064428, .name = "VT1718S", + .patch = patch_vt1718S}, {} /* terminator */ }; -- cgit v0.10.2 From bb3c6bfc3f7a5416d85c5dbc312e2d47fc672eef Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:39 +0800 Subject: ALSA: HDA VIA: Add VT1828S and VT2020 support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c783853..2e7e72c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -179,6 +179,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x428 && (dev_id >> 12) < 8) codec_type = VT1718S; + else if (dev_id == 0x0441 || dev_id == 0x4441) + codec_type = VT1718S; else codec_type = UNKNOWN; return codec_type; @@ -4323,21 +4325,31 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - spec->stream_name_analog = "VT1718S Analog"; + if (codec->vendor_id == 0x11060441) + spec->stream_name_analog = "VT2020 Analog"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_analog = "VT1828S Analog"; + else + spec->stream_name_analog = "VT1718S Analog"; spec->stream_analog_playback = &vt1718S_pcm_analog_playback; spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - spec->stream_name_digital = "VT1718S Digital"; + if (codec->vendor_id == 0x11060441) + spec->stream_name_digital = "VT2020 Digital"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_digital = "VT1828S Digital"; + else + spec->stream_name_digital = "VT1718S Digital"; spec->stream_digital_playback = &vt1718S_pcm_digital_playback; - if (codec->vendor_id == 0x11060428) + if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) spec->stream_digital_capture = &vt1718S_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1718S_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; spec->num_mixers++; } @@ -4429,6 +4441,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1718S}, { .id = 0x11064428, .name = "VT1718S", .patch = patch_vt1718S}, + { .id = 0x11060441, .name = "VT2020", + .patch = patch_vt1718S}, + { .id = 0x11064441, .name = "VT1828S", + .patch = patch_vt1718S}, {} /* terminator */ }; -- cgit v0.10.2 From f3db423df84570c9950754a5771ad26f0111235f Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:41 +0800 Subject: ALSA: HDA VIA: Add VT1716S support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2e7e72c..2977004 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -87,12 +87,13 @@ enum VIA_HDA_CODEC { VT1708BCE, VT1702, VT1718S, + VT1716S, CODEC_TYPES, }; struct via_spec { /* codec parameterization */ - struct snd_kcontrol_new *mixers[4]; + struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; struct hda_verb *init_verbs[5]; @@ -135,7 +136,7 @@ struct via_spec { unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; unsigned int smart51_enabled; - + unsigned int dmic_enabled; enum VIA_HDA_CODEC codec_type; /* work to check hp jack state */ @@ -179,6 +180,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x428 && (dev_id >> 12) < 8) codec_type = VT1718S; + else if (dev_id == 0x0433 || dev_id == 0xa721) + codec_type = VT1716S; else if (dev_id == 0x0441 || dev_id == 0x4441) codec_type = VT1718S; else @@ -189,6 +192,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 #define VIA_JACK_EVENT 0x04 +#define VIA_MONO_EVENT 0x08 enum { VIA_CTL_WIDGET_VOL, @@ -295,6 +299,11 @@ static hda_nid_t vt1718S_adc_nids[2] = { 0x10, 0x11 }; +static hda_nid_t vt1716S_adc_nids[2] = { + /* ADC1-2 */ + 0x13, 0x14 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -584,6 +593,106 @@ static void set_jack_power_state(struct hda_codec *codec) snd_hda_codec_write(codec, 0xc, 0, AC_VERB_SET_POWER_STATE, parm); } + } else if (spec->codec_type == VT1716S) { + unsigned int mono_out, present; + /* SW0 (17h) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW0(13h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1e, &parm); + /* PW11 (22h) */ + if (spec->dmic_enabled) + set_pin_power_state(codec, 0x22, &parm); + else + snd_hda_codec_write( + codec, 0x22, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + /* SW2(26h), AIW1(14h) */ + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + /* Smart 5.1 PW2(1bh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1b, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW7 (23h), SW3 (27h), AOW3 (25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x23, &parm); + /* Smart 5.1 PW1(1ah) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1a, &parm); + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Smart 5.1 PW5(1eh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1e, &parm); + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Mono out */ + /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ + present = snd_hda_codec_read( + codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) + mono_out = 0; + else { + present = snd_hda_codec_read( + codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) + & 0x80000000; + if (!spec->hp_independent_mode && present) + mono_out = 0; + else + mono_out = 1; + } + parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW 3/4 (1ch/1dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + /* HP Independent Mode, power on AOW3 */ + if (spec->hp_independent_mode) + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* force to D0 for internal Speaker */ + /* MW0 (16h), AOW0 (10h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + mono_out ? AC_PWRST_D0 : parm); } } @@ -738,7 +847,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S || spec->codec_type == VT1702 - || spec->codec_type == VT1718S) { + || spec->codec_type == VT1718S + || spec->codec_type == VT1716S) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -797,6 +907,7 @@ static void mute_aa_path(struct hda_codec *codec, int mute) case VT1708B_8CH: case VT1708B_4CH: case VT1708S: + case VT1716S: nid_mixer = 0x16; start_idx = 2; end_idx = 4; @@ -898,7 +1009,8 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, HDA_AMP_UNMUTE); } if (i == AUTO_PIN_FRONT_MIC) { - if (spec->codec_type == VT1708S) { + if (spec->codec_type == VT1708S + || spec->codec_type == VT1716S) { /* input = index 1 (AOW3) */ snd_hda_codec_write( codec, nid, 0, @@ -961,6 +1073,7 @@ static int is_aa_path_mute(struct hda_codec *codec) case VT1708B_8CH: case VT1708B_4CH: case VT1708S: + case VT1716S: nid_mixer = 0x16; start_idx = 2; end_idx = 4; @@ -1025,6 +1138,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) break; case VT1708S: case VT1718S: + case VT1716S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; @@ -1453,6 +1567,36 @@ static void via_hp_automute(struct hda_codec *codec) } } +/* mute mono out if HP or Line out is plugged */ +static void via_mono_automute(struct hda_codec *codec) +{ + unsigned int hp_present, lineout_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1716S) + return; + + lineout_present = snd_hda_codec_read( + codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + /* Mute Mono Out if Line Out is plugged */ + if (lineout_present) { + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); + return; + } + + hp_present = snd_hda_codec_read( + codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, + hp_present ? HDA_AMP_MUTE : 0); +} + static void via_gpio_control(struct hda_codec *codec) { unsigned int gpio_data; @@ -1512,6 +1656,8 @@ static void via_unsol_event(struct hda_codec *codec, via_gpio_control(codec); if (res & VIA_JACK_EVENT) set_jack_power_state(codec); + if (res & VIA_MONO_EVENT) + via_mono_automute(codec); } static int via_init(struct hda_codec *codec) @@ -4365,6 +4511,496 @@ static int patch_vt1718S(struct hda_codec *codec) return 0; } + +/* Patch for VT1716S */ + +static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *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 vt1716s_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int index = 0; + + index = snd_hda_codec_read(codec, 0x26, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (index != -1) + *ucontrol->value.integer.value = index; + + return 0; +} + +static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index = *ucontrol->value.integer.value; + + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_CONNECT_SEL, index); + spec->dmic_enabled = index; + set_jack_power_state(codec); + + return 1; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1716S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Mic Capture Switch", + .count = 1, + .info = vt1716s_dmic_info, + .get = vt1716s_dmic_get, + .put = vt1716s_dmic_put, + }, + {} /* end */ +}; + + +/* mono-out mixer elements */ +static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { + HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static struct hda_verb vt1716S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Stereo Mixer = 5 */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, + + /* Setup default input of PW4 to MW0 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, + + /* Setup default input of SW1 as MW0 */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* Setup default input of SW4 as AOW0 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* PW9 PW10 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + /* Unmute SW1, PW12 */ + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* PW12 Output enable */ + {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf8a, 0x80}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, + /* Enable mono output */ + {0x1, 0xf90, 0x08}, + { } +}; + + +static struct hda_verb vt1716S_uniwill_init_verbs[] = { + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 6, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x13, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 3; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x25; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[3] = { "Front", "Surround", "C/LFE" }; + hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; + hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; + hda_nid_t nid, nid_vol, nid_mute; + int i, err; + + for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x25; /* AOW3 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1a: /* Mic */ + idx = 2; + break; + + case 0x1b: /* Line In */ + idx = 3; + break; + + case 0x1e: /* Front Mic */ + idx = 4; + break; + + case 0x1f: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x16); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx-1; + imux->num_items++; + } + return 0; +} + +static int vt1716S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1716S_loopbacks[] = { + { 0x16, HDA_INPUT, 1 }, + { 0x16, HDA_INPUT, 2 }, + { 0x16, HDA_INPUT, 3 }, + { 0x16, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1716S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1716S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; + + spec->stream_name_analog = "VT1716S Analog"; + spec->stream_analog_playback = &vt1716S_pcm_analog_playback; + spec->stream_analog_capture = &vt1716S_pcm_analog_capture; + + spec->stream_name_digital = "VT1716S Digital"; + spec->stream_digital_playback = &vt1716S_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1716S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; + spec->num_mixers++; + } + + spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; + spec->num_mixers++; + + spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1716S_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -4445,6 +5081,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1718S}, { .id = 0x11064441, .name = "VT1828S", .patch = patch_vt1718S}, + { .id = 0x11060433, .name = "VT1716S", + .patch = patch_vt1716S}, + { .id = 0x1106a721, .name = "VT1716S", + .patch = patch_vt1716S}, {} /* terminator */ }; -- cgit v0.10.2 From 25eaba2f8a6877ba6f58197c4723c2433a316e09 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:43 +0800 Subject: ALSA: HDA VIA: Add VT2002P support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2977004..a94cc91 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -88,6 +88,7 @@ enum VIA_HDA_CODEC { VT1702, VT1718S, VT1716S, + VT2002P, CODEC_TYPES, }; @@ -184,6 +185,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1716S; else if (dev_id == 0x0441 || dev_id == 0x4441) codec_type = VT1718S; + else if (dev_id == 0x0438 || dev_id == 0x4438) + codec_type = VT2002P; else codec_type = UNKNOWN; return codec_type; @@ -193,11 +196,14 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_GPIO_EVENT 0x02 #define VIA_JACK_EVENT 0x04 #define VIA_MONO_EVENT 0x08 +#define VIA_SPEAKER_EVENT 0x10 +#define VIA_BIND_HP_EVENT 0x20 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, + VIA_CTL_WIDGET_BIND_PIN_MUTE, }; enum { @@ -235,6 +241,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) flush_scheduled_work(); } + static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -262,13 +269,108 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, .put = analog_input_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } +static void via_hp_bind_automute(struct hda_codec *codec); + +static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int i; + int change = 0; + + long *valp = ucontrol->value.integer.value; + int lmute, rmute; + if (strstr(kcontrol->id.name, "Switch") == NULL) { + snd_printd("Invalid control!\n"); + return change; + } + change = snd_hda_mixer_amp_switch_put(kcontrol, + ucontrol); + /* Get mute value */ + lmute = *valp ? 0 : HDA_AMP_MUTE; + valp++; + rmute = *valp ? 0 : HDA_AMP_MUTE; + + /* Set hp pins */ + if (!spec->hp_independent_mode) { + for (i = 0; i < spec->autocfg.hp_outs; i++) { + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } + + if (!lmute && !rmute) { + /* Line Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* unmute */ + via_hp_bind_automute(codec); + + } else { + if (lmute) { + /* Mute all left channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + } + if (rmute) { + /* mute all right channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } + return change; +} + +#define BIND_PIN_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = bind_pin_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + static struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, + BIND_PIN_MUTE, }; - static hda_nid_t vt1708_adc_nids[2] = { /* ADC1-2 */ 0x15, 0x27 @@ -304,6 +406,11 @@ static hda_nid_t vt1716S_adc_nids[2] = { 0x13, 0x14 }; +static hda_nid_t vt2002P_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -386,10 +493,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; hda_nid_t pin; + int i; - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + for (i = 0; i < spec->autocfg.hp_outs; i++) { + pin = spec->autocfg.hp_pins[i]; + if (pin) /* connect to front */ + via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + } } static void via_auto_init_analog_input(struct hda_codec *codec) @@ -693,6 +803,107 @@ static void set_jack_power_state(struct hda_codec *codec) imux_is_smixer ? AC_PWRST_D0 : parm); snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, mono_out ? AC_PWRST_D0 : parm); + } else if (spec->codec_type == VT2002P) { + unsigned int present; + /* MUX9 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (26h), MW4 (1ch), MUX4(37h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x37, + 0, AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Class-D */ + /* PW0 (24h), MW0(18h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* Mono Out */ + /* PW15 (31h), MW8(17h), MUX8(3bh) */ + present = snd_hda_codec_read( + codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } } @@ -760,6 +971,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, case VT1718S: nid = 0x34; break; + case VT2002P: + nid = 0x35; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -832,6 +1046,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ spec->multiout.num_dacs = 4; break; + case VT2002P: + nid = 0x35; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -848,7 +1065,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, if (spec->codec_type == VT1708S || spec->codec_type == VT1702 || spec->codec_type == VT1718S - || spec->codec_type == VT1716S) { + || spec->codec_type == VT1716S + || spec->codec_type == VT2002P) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -1088,6 +1306,11 @@ static int is_aa_path_mute(struct hda_codec *codec) start_idx = 1; end_idx = 3; break; + case VT2002P: + nid_mixer = 0x21; + start_idx = 0; + end_idx = 2; + break; default: return 0; } @@ -1146,6 +1369,10 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) verb = 0xf73; parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; + case VT2002P: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; default: return; /* other codecs are not supported */ } @@ -1645,6 +1872,66 @@ static void via_gpio_control(struct hda_codec *codec) } } +/* mute Internal-Speaker if HP is plugged */ +static void via_speaker_automute(struct hda_codec *codec) +{ + unsigned int hp_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT2002P) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Speaker Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +/* mute line-out and internal speaker if HP is plugged */ +static void via_hp_bind_automute(struct hda_codec *codec) +{ + unsigned int hp_present, present = 0; + struct via_spec *spec = codec->spec; + int i; + + if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + /* Mute Line-Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + if (hp_present) + present = hp_present; + } + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + /* unsolicited event for jack sensing */ static void via_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1658,6 +1945,10 @@ static void via_unsol_event(struct hda_codec *codec, set_jack_power_state(codec); if (res & VIA_MONO_EVENT) via_mono_automute(codec); + if (res & VIA_SPEAKER_EVENT) + via_speaker_automute(codec); + if (res & VIA_BIND_HP_EVENT) + via_hp_bind_automute(codec); } static int via_init(struct hda_codec *codec) @@ -2067,10 +2358,19 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static int via_auto_init(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; + via_init(codec); via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); + if (spec->codec_type == VT2002P) { + via_hp_bind_automute(codec); + } else { + via_hp_automute(codec); + via_speaker_automute(codec); + } + return 0; } @@ -5001,6 +5301,359 @@ static int patch_vt1716S(struct hda_codec *codec) return 0; } + +/* for vt2002P */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt2002P_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt2002P_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/8 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x37, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, + + /* set PW0 index=0 (MW0) */ + {0x24, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0x88}, + { } +}; + + +static struct hda_verb vt2002P_uniwill_init_verbs[] = { + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 3; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 4; + imux->num_items++; + + return 0; +} + +static int vt2002P_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt2002P_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt2002P */ +static int patch_vt2002P(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt2002P_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; + + spec->stream_name_analog = "VT2002P Analog"; + spec->stream_analog_playback = &vt2002P_pcm_analog_playback; + spec->stream_analog_capture = &vt2002P_pcm_analog_capture; + + spec->stream_name_digital = "VT2002P Digital"; + spec->stream_digital_playback = &vt2002P_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt2002P_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt2002P_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -5085,6 +5738,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1716S}, { .id = 0x1106a721, .name = "VT1716S", .patch = patch_vt1716S}, + { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, {} /* terminator */ }; -- cgit v0.10.2 From ab6734e7ea32e9f9cbe0f55eeddf4aa629ed1c3d Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:46 +0800 Subject: ALSA: HDA VIA: Add VT1812 support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a94cc91..b3c5e8a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -89,6 +89,7 @@ enum VIA_HDA_CODEC { VT1718S, VT1716S, VT2002P, + VT1812, CODEC_TYPES, }; @@ -187,6 +188,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1718S; else if (dev_id == 0x0438 || dev_id == 0x4438) codec_type = VT2002P; + else if (dev_id == 0x0448) + codec_type = VT1812; else codec_type = UNKNOWN; return codec_type; @@ -411,6 +414,12 @@ static hda_nid_t vt2002P_adc_nids[2] = { 0x10, 0x11 }; +static hda_nid_t vt1812_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -904,6 +913,120 @@ static void set_jack_power_state(struct hda_codec *codec) snd_hda_codec_write( codec, 0x21, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else if (spec->codec_type == VT1812) { + unsigned int present; + /* MUX10 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (28h), MW4 (18h), MUX4(38h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x38, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Internal Speaker */ + /* PW0 (24h), MW0(14h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + /* Mono Out */ + /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ + present = snd_hda_codec_read( + codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + + /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x33, &parm); + snd_hda_codec_write(codec, 0x1d, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3d, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } } @@ -974,6 +1097,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, case VT2002P: nid = 0x35; break; + case VT1812: + nid = 0x3d; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -1049,6 +1175,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, case VT2002P: nid = 0x35; break; + case VT1812: + nid = 0x3d; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -1066,7 +1195,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, || spec->codec_type == VT1702 || spec->codec_type == VT1718S || spec->codec_type == VT1716S - || spec->codec_type == VT2002P) { + || spec->codec_type == VT2002P + || spec->codec_type == VT1812) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -1307,6 +1437,7 @@ static int is_aa_path_mute(struct hda_codec *codec) end_idx = 3; break; case VT2002P: + case VT1812: nid_mixer = 0x21; start_idx = 0; end_idx = 2; @@ -1370,6 +1501,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; case VT2002P: + case VT1812: verb = 0xf93; parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ break; @@ -1878,7 +2010,7 @@ static void via_speaker_automute(struct hda_codec *codec) unsigned int hp_present; struct via_spec *spec = codec->spec; - if (spec->codec_type != VT2002P) + if (spec->codec_type != VT2002P && spec->codec_type != VT1812) return; hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, @@ -2364,7 +2496,7 @@ static int via_auto_init(struct hda_codec *codec) via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); - if (spec->codec_type == VT2002P) { + if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { via_hp_bind_automute(codec); } else { via_hp_automute(codec); @@ -5654,6 +5786,361 @@ static int patch_vt2002P(struct hda_codec *codec) return 0; } + +/* for vt1812 */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1812_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1812_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/13/15 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x38, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0xa8}, + { } +}; + + +static struct hda_verb vt1812_uniwill_init_verbs[] = { + {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1812_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1812_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + + +/* add playback controls from the parsed DAC table */ +static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 6; + imux->num_items++; + + return 0; +} + +static int vt1812_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + fill_dig_outs(codec); + err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) + return 0; /* can't find valid BIOS pin config */ + + err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1812_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt1812 */ +static int patch_vt1812(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1812_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + + spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; + + spec->stream_name_analog = "VT1812 Analog"; + spec->stream_analog_playback = &vt1812_pcm_analog_playback; + spec->stream_analog_capture = &vt1812_pcm_analog_capture; + + spec->stream_name_digital = "VT1812 Digital"; + spec->stream_digital_playback = &vt1812_pcm_digital_playback; + + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1812_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1812_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1812_loopbacks; +#endif + + return 0; +} + /* * patch entries */ @@ -5740,6 +6227,7 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1716S}, { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, {} /* terminator */ }; -- cgit v0.10.2 From 71eb7dccb7d2d22236dbe46db07f8000d09fba01 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:49 +0800 Subject: ALSA: HDA VIA: rename vt1708_control_templates[]. To via_control_templates[]. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b3c5e8a..257b51c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -367,7 +367,7 @@ static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, .put = bind_pin_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } -static struct snd_kcontrol_new vt1708_control_templates[] = { +static struct snd_kcontrol_new via_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, @@ -430,7 +430,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, knew = snd_array_new(&spec->kctls); if (!knew) return -ENOMEM; - *knew = vt1708_control_templates[type]; + *knew = via_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; -- cgit v0.10.2 From bfdc675a73f7697ead12c07dbf11e2b2632676f4 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:50 +0800 Subject: ALSA: HDA VIA: Change PW4 connect select default to to MW0. According to customer request, hp should be default to redirected mode, i.e. PW4 connect select default to to MW0. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 257b51c..4ea18a7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1541,8 +1541,8 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Setup default input to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Setup default input MW0 to PW4 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -2668,8 +2668,8 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set input of PW4 as AOW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Set input of PW4 as MW0 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -3222,7 +3222,7 @@ static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PW10 Input enable */ -- cgit v0.10.2 From 8e86597f3cbd0a58808560116abe1bc0023256b0 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:52 +0800 Subject: ALSA: HDA VIA: comments: update copyright, changeset, etc. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4ea18a7..fab875a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1,10 +1,10 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec + * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec * - * Copyright (c) 2006-2008 Lydia Wang - * Takashi Iwai + * (C) 2006-2009 VIA Technology, Inc. + * (C) 2006-2008 Takashi Iwai * * 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 @@ -36,6 +36,11 @@ /* 2008-04-09 Lydia Wang Add Independent HP feature */ /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ +/* 2009-02-16 Logan Li Add support for VT1718S */ +/* 2009-03-13 Logan Li Add support for VT1716S */ +/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ +/* 2009-07-08 Lydia Wang Add support for VT2002P */ +/* 2009-07-21 Lydia Wang Add support for VT1812 */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -- cgit v0.10.2 From 377ff31ae06f0d2644839246cd18c3e17fe62a48 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:55 +0800 Subject: ALSA: HDA VIA: Only cosmetic changes Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fab875a..30260e2 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -22,26 +22,26 @@ */ /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ -/* */ +/* */ /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ -/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ -/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ +/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ +/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ -/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ -/* 2007-09-17 Lydia Wang Add VT1708B codec support */ +/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ +/* 2007-09-17 Lydia Wang Add VT1708B codec support */ /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ -/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ -/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ -/* 2008-04-09 Lydia Wang Add Independent HP feature */ +/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ +/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ +/* 2008-04-09 Lydia Wang Add Independent HP feature */ /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ -/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ +/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ /* 2009-02-16 Logan Li Add support for VT1718S */ /* 2009-03-13 Logan Li Add support for VT1716S */ /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ /* 2009-07-08 Lydia Wang Add support for VT2002P */ /* 2009-07-21 Lydia Wang Add support for VT1812 */ -/* */ +/* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -486,7 +486,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } @@ -1545,7 +1545,7 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - + /* Setup default input MW0 to PW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ @@ -1865,8 +1865,10 @@ static int via_build_pcms(struct hda_codec *codec) codec->pcm_info = info; 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_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]; @@ -2116,7 +2118,7 @@ static int via_init(struct hda_codec *codec) if (spec->slave_dig_outs[0]) codec->slave_dig_outs = spec->slave_dig_outs; - return 0; + return 0; } #ifdef SND_HDA_NEEDS_RESUME @@ -2161,8 +2163,8 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.num_dacs = cfg->line_outs; spec->multiout.dac_nids = spec->private_dac_nids; - - for(i = 0; i < 4; i++) { + + for (i = 0; i < 4; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ @@ -2200,7 +2202,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; - + nid_vol = nid_vols[i]; if (i == AUTO_SEQ_CENLFE) { @@ -2229,7 +2231,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ + } else if (i == AUTO_SEQ_FRONT) { /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", @@ -2243,7 +2245,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -2343,7 +2345,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -2576,7 +2578,7 @@ static int patch_vt1708(struct hda_codec *codec) "from BIOS. Using genenic mode...\n"); } - + spec->stream_name_analog = "VT1708 Analog"; spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ @@ -2588,7 +2590,7 @@ static int patch_vt1708(struct hda_codec *codec) spec->stream_digital_playback = &vt1708_pcm_digital_playback; spec->stream_digital_capture = &vt1708_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1708_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); @@ -2775,11 +2777,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ } else if (cfg->line_outs == 3) { /* 6 channels */ - for(i = 0; i < cfg->line_outs; i++) { + for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ - switch(i) { + switch (i) { case AUTO_SEQ_FRONT: /* AOW0 */ spec->multiout.dac_nids[i] = 0x10; @@ -2814,7 +2816,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, for (i = 0; i <= AUTO_SEQ_SIDE; i++) { nid = cfg->line_out_pins[i]; - if (!nid) + if (!nid) continue; nid_vol = nid_vols[i]; @@ -2845,7 +2847,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ + } else if (i == AUTO_SEQ_FRONT) { /* ADD control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", @@ -2859,7 +2861,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -2955,7 +2957,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -3064,7 +3066,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); @@ -3158,7 +3160,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); -- cgit v0.10.2 From 0f48327eac5f65ad029d7112cac97577766730ba Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 12 Oct 2009 15:56:17 +1100 Subject: sound: use semicolons to end statements Fixes: sound/pci/hda/patch_via.c: In function 'patch_vt1718S': sound/pci/hda/patch_via.c:4951: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt1716S': sound/pci/hda/patch_via.c:5441: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt2002P': sound/pci/hda/patch_via.c:5794: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt1812': sound/pci/hda/patch_via.c:6148: error: expected expression before 'return' Signed-off-by: Stephen Rothwell Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 30260e2..a294060 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -4942,7 +4942,7 @@ static int patch_vt1718S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1718S_loopbacks; @@ -5432,7 +5432,7 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1716S_loopbacks; @@ -5785,7 +5785,7 @@ static int patch_vt2002P(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt2002P_loopbacks; @@ -6139,7 +6139,7 @@ static int patch_vt1812(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1812_loopbacks; -- cgit v0.10.2 From d2ed82a3e7d1f63b2da3f1aa5763667dd17919ac Mon Sep 17 00:00:00 2001 From: Logan Li Date: Wed, 14 Oct 2009 10:10:38 +0800 Subject: ALSA: HDA VIA: Remove 48k sample rate limit for S/PDIF 48 kHz limit is for slightly better stability, and sample rates other than 48k (like 96k/192k) are for better sound quality. We choose better quality, so remove the 48k limit. Signed-off-by: Logan Li Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a294060..89e084d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -4626,7 +4626,6 @@ static struct hda_pcm_stream vt1718S_pcm_digital_playback = { .substreams = 2, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5124,7 +5123,6 @@ static struct hda_pcm_stream vt1716S_pcm_digital_playback = { .substreams = 2, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5561,7 +5559,6 @@ static struct hda_pcm_stream vt2002P_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5914,7 +5911,6 @@ static struct hda_pcm_stream vt1812_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, -- cgit v0.10.2 From 739b47f1e5aa3b36eadd7906cc6b41f0175c6ed1 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:34:19 +0100 Subject: ALSA: hda - select IbexPeak handler for Calpella An earlier patch merely adds id for 0x80862804. It has 2/3 cvt/pin nodes and shall be tied to the IbexPeak handler. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 01a18ed..7c23016 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -684,7 +684,7 @@ static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ -- cgit v0.10.2 From f5d6def5c642587434c42722c57fb65642f61038 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:38:26 +0100 Subject: ALSA: hda - vectorize get_empty_pcm_device() This unifies the code and data structure, and makes it easy to add more HDMI devices. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index af989f6..49289cd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2885,43 +2885,26 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) static const char *dev_name[HDA_PCM_NTYPES] = { "Audio", "SPDIF", "HDMI", "Modem" }; - /* starting device index for each PCM type */ - static int dev_idx[HDA_PCM_NTYPES] = { - [HDA_PCM_TYPE_AUDIO] = 0, - [HDA_PCM_TYPE_SPDIF] = 1, - [HDA_PCM_TYPE_HDMI] = 3, - [HDA_PCM_TYPE_MODEM] = 6 + /* audio device indices; not linear to keep compatibility */ + static int audio_idx[HDA_PCM_NTYPES][5] = { + [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, + [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; - int i, dev; - - switch (type) { - case HDA_PCM_TYPE_AUDIO: - for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { - dev = audio_idx[i]; - if (!test_bit(dev, bus->pcm_dev_bits)) - goto ok; - } - snd_printk(KERN_WARNING "Too many audio devices\n"); - return -EAGAIN; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - dev = dev_idx[type]; - if (test_bit(dev, bus->pcm_dev_bits)) { - snd_printk(KERN_WARNING "%s already defined\n", - dev_name[type]); - return -EAGAIN; - } - break; - default: + int i; + + if (type >= HDA_PCM_NTYPES) { snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); return -EINVAL; } - ok: - set_bit(dev, bus->pcm_dev_bits); - return dev; + + for (i = 0; audio_idx[type][i] >= 0 ; i++) + if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) + return audio_idx[type][i]; + + snd_printk(KERN_WARNING "Too many %s devices\n", dev_name[type]); + return -EAGAIN; } /* -- cgit v0.10.2 From 92608badc519a8c1f65d93743396517aaa582b53 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:40:03 +0100 Subject: ALSA: hda - allow up to 4 HDMI devices The new Intel HDMI codec supports 2 independant HDMI/DisplayPort pipes. We'll be exporting them as 2 pcm devices. So bump up the allowed number of HDMI devices. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 49289cd..2c13663 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2889,7 +2889,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, - [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; int i; -- cgit v0.10.2 From 6797cf2bfcbf2fa1fd05c0b785dc1402f73e2ce5 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:40:40 +0100 Subject: ALSA: hda - convert intelhdmi global references to local parameters No behavior change. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 7c23016..2dfb1ef 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -189,35 +189,36 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { */ #ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int *packet_index, int *byte_index) { int val; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0); + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); *packet_index = val >> 5; *byte_index = val & 0x1f; } #endif -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char val) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); } -static void hdmi_enable_output(struct hda_codec *codec) +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) @@ -231,7 +232,8 @@ static void hdmi_enable_output(struct hda_codec *codec) /* * Enable Audio InfoFrame Transmission */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec) +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, @@ -241,37 +243,40 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec) /* * Disable Audio InfoFrame Transmission */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec) +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); } -static int hdmi_get_channel_count(struct hda_codec *codec) +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, + return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } -static void hdmi_set_channel_count(struct hda_codec *codec, int chs) +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); - if (chs != hdmi_get_channel_count(codec)) +#ifdef CONFIG_SND_DEBUG_VERBOSE + if (chs != hdmi_get_channel_count(codec, nid)) snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec)); + chs, hdmi_get_channel_count(codec, nid)); +#endif } -static void hdmi_debug_channel_mapping(struct hda_codec *codec) +static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; int slot; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, cvt_nid, 0, + slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0x7); @@ -293,7 +298,7 @@ static void hdmi_parse_eld(struct hda_codec *codec) * Audio InfoFrame routines */ -static void hdmi_debug_dip_size(struct hda_codec *codec) +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; @@ -310,7 +315,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec) #endif } -static void hdmi_clear_dip_buffers(struct hda_codec *codec) +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef BE_PARANOID int i, j; @@ -340,14 +345,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec) } static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) { u8 *params = (u8 *)ai; u8 sum = 0; int i; - hdmi_debug_dip_size(codec); - hdmi_clear_dip_buffers(codec); /* be paranoid */ + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ for (i = 0; i < sizeof(ai); i++) sum += params[i]; @@ -386,7 +392,7 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; @@ -439,8 +445,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, return ai->CA; } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) +static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) { int i; @@ -453,15 +459,15 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, */ for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, cvt_nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); - hdmi_debug_channel_mapping(codec); + hdmi_debug_channel_mapping(codec, nid); } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { struct hdmi_audio_infoframe ai = { @@ -471,11 +477,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, .CC02_CT47 = substream->runtime->channels - 1, }; - hdmi_setup_channel_allocation(codec, &ai); - hdmi_setup_channel_mapping(codec, &ai); + hdmi_setup_channel_allocation(codec, nid, &ai); + hdmi_setup_channel_mapping(codec, nid, &ai); - hdmi_fill_audio_infoframe(codec, &ai); - hdmi_start_infoframe_trans(codec); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); } @@ -553,7 +559,7 @@ static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, { struct intel_hdmi_spec *spec = codec->spec; - hdmi_stop_infoframe_trans(codec); + hdmi_stop_infoframe_trans(codec, pin_nid); return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -569,9 +575,9 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream); - hdmi_set_channel_count(codec, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, substream); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream); return 0; } @@ -619,7 +625,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec); + hdmi_enable_output(codec, pin_nid); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, -- cgit v0.10.2 From 7bedb011ef4db93b15049ece8d50b29d6fe6af9d Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:41:44 +0100 Subject: ALSA: hda - remove intelhdmi dependency on multiout We'll be managing multiple HDMI audio sources/sinks on our own. So remove multiout dependency from intelhdmi. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 2dfb1ef..02be428 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -39,7 +39,6 @@ static hda_nid_t pin_nid; /* HDMI output pin */ #define INTEL_HDMI_EVENT_TAG 0x08 struct intel_hdmi_spec { - struct hda_multi_out multiout; struct hda_pcm pcm_rec; struct hdmi_eld sink_eld; }; @@ -548,9 +547,7 @@ static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); + return 0; } static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, @@ -561,7 +558,8 @@ static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, hdmi_stop_infoframe_trans(codec, pin_nid); - return snd_hda_multi_out_dig_close(codec, &spec->multiout); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + return 0; } static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -570,15 +568,12 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); - - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels); hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; } @@ -616,7 +611,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) struct intel_hdmi_spec *spec = codec->spec; int err; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); if (err < 0) return err; @@ -657,10 +652,6 @@ static int do_patch_intel_hdmi(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = cvt_nid; - codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops; -- cgit v0.10.2 From 70ca35fb42680fc4315d4a01f6c77c9a9962199c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:42:18 +0100 Subject: ALSA: hda - use pcm prepare/cleanup callbacks for intelhdmi Remove pcm callbacks open/close in favor of the prepare/cleanup. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 02be428..c17feac 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -543,16 +543,9 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; @@ -582,9 +575,8 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { .channels_min = 2, .channels_max = 8, .ops = { - .open = intel_hdmi_playback_pcm_open, - .close = intel_hdmi_playback_pcm_close, - .prepare = intel_hdmi_playback_pcm_prepare + .prepare = intel_hdmi_playback_pcm_prepare, + .cleanup = intel_hdmi_playback_pcm_cleanup, }, }; -- cgit v0.10.2 From ddb8152b054e357907f57fb5ae65d494a3c79065 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:43:03 +0100 Subject: ALSA: hda - reorder intelhdmi prepare/cleanup callbacks No behavior change. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index c17feac..6be5ca4 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -543,30 +543,30 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels); - hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream); - snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; } -static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, - substream->runtime->channels); + struct intel_hdmi_spec *spec = codec->spec; - hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_stop_infoframe_trans(codec, pin_nid); - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v0.10.2 From 54a25f87e943fc77f57e86849897ad6602519286 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:44:26 +0100 Subject: ALSA: hda - vectorize intelhdmi The Intel IbexPeak HDMI codec supports 2 converters and 3 pins, which requires converting the cvt_nid/pin_nid to arrays. The active pin number (the one connected with a live HDMI monitor/sink) will be dynamically identified on hotplug events. It exports two HDMI devices, so that user space can choose the A/V pipe for sending the audio samples. It's still undefined behavior when there are two active monitors connected and routed to the same audio converter. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 9446a5a..20fa6ae 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, } -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index) { char name[32]; struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "eld#%d", codec->addr); + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); err = snd_card_proc_new(codec->bus->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5f1dcc5..461e0c1 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -541,11 +541,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); void snd_hdmi_show_eld(struct hdmi_eld *eld); #ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld); +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); #else static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld) + struct hdmi_eld *eld, + int index) { return 0; } diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 6be5ca4..08ea88d 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -33,14 +33,43 @@ #include "hda_codec.h" #include "hda_local.h" -static hda_nid_t cvt_nid; /* audio converter */ -static hda_nid_t pin_nid; /* HDMI output pin */ +/* + * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device + * could support two independent pipes, each of them can be connected to one or + * more ports (DVI, HDMI or DisplayPort). + * + * The HDA correspondence of pipes/ports are converter/pin nodes. + */ +#define INTEL_HDMI_CVTS 2 +#define INTEL_HDMI_PINS 3 -#define INTEL_HDMI_EVENT_TAG 0x08 +static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = { + "INTEL HDMI 0", + "INTEL HDMI 1", +}; struct intel_hdmi_spec { - struct hda_pcm pcm_rec; - struct hdmi_eld sink_eld; + int num_cvts; + int num_pins; + hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */ + + /* + * source connection for each pin + */ + hda_nid_t pin_cvt[INTEL_HDMI_PINS+1]; + + /* + * HDMI sink attached to each pin + */ + bool sink_present[INTEL_HDMI_PINS]; + bool sink_eldv[INTEL_HDMI_PINS]; + struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; + + /* + * export one pcm per pipe + */ + struct hda_pcm pcm_rec[INTEL_HDMI_CVTS]; }; struct hdmi_audio_infoframe { @@ -183,6 +212,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, }; + +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + /* * HDMI routines */ @@ -283,12 +325,12 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) #endif } -static void hdmi_parse_eld(struct hda_codec *codec) +static void hdmi_parse_eld(struct hda_codec *codec, int index) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld = &spec->sink_eld[index]; - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) snd_hdmi_show_eld(eld); } @@ -395,7 +437,7 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld; int i; int spk_mask = 0; int channels = 1 + (ai->CC02_CT47 & 0x7); @@ -407,6 +449,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, if (channels <= 2) return 0; + i = hda_node_index(spec->pin_cvt, nid); + if (i < 0) + return 0; + eld = &spec->sink_eld[i]; + /* * HDMI sink's ELD info cannot always be retrieved for now, e.g. * in console or for audio devices. Assume the highest speakers @@ -469,6 +516,9 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; struct hdmi_audio_infoframe ai = { .type = 0x84, .ver = 0x01, @@ -479,8 +529,16 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, hdmi_setup_channel_allocation(codec, nid, &ai); hdmi_setup_channel_mapping(codec, nid, &ai); - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (spec->sink_present[i] != true) + continue; + + pin_nid = spec->pin[i]; + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } } @@ -490,27 +548,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int pind = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); + int index; printk(KERN_INFO - "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", - pind, eldv); + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_present[index] = pind; + spec->sink_eldv[index] = eldv; if (pind && eldv) { - hdmi_parse_eld(codec); + hdmi_parse_eld(codec, index); /* TODO: do real things about ELD */ } } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) { + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); printk(KERN_INFO - "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, subtag, cp_state, cp_ready); @@ -525,10 +595,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (tag != INTEL_HDMI_EVENT_TAG) { + if (hda_node_index(spec->pin, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -549,10 +620,10 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, + hdmi_set_channel_count(codec, hinfo->nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; @@ -563,8 +634,14 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != hinfo->nid) + continue; - hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_stop_infoframe_trans(codec, spec->pin[i]); + } snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; @@ -583,17 +660,19 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { static int intel_hdmi_build_pcms(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; + int i; - codec->num_pcms = 1; + codec->num_pcms = spec->num_cvts; codec->pcm_info = info; - /* NID to query formats and rates and setup streams */ - intel_hdmi_pcm_playback.nid = cvt_nid; - - info->name = "INTEL HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; + for (i = 0; i < codec->num_pcms; i++, info++) { + info->name = intel_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + intel_hdmi_pcm_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + } return 0; } @@ -602,29 +681,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; int err; + int i; - err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); - if (err < 0) - return err; + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + if (err < 0) + return err; + } return 0; } static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec, pin_nid); + struct intel_hdmi_spec *spec = codec->spec; + int i; - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); + for (i = 0; spec->pin[i]; i++) { + hdmi_enable_output(codec, spec->pin[i]); + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | spec->pin[i]); + } return 0; } static void intel_hdmi_free(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); - snd_hda_eld_proc_free(codec, &spec->sink_eld); kfree(spec); } @@ -636,18 +725,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static int do_patch_intel_hdmi(struct hda_codec *codec) +static struct intel_hdmi_spec static_specs[] = { + { + .num_cvts = 1, + .num_pins = 1, + .cvt = { 0x2 }, + .pin = { 0x3 }, + .pin_cvt = { 0x2 }, + }, + { + .num_cvts = 2, + .num_pins = 3, + .cvt = { 0x2, 0x3 }, + .pin = { 0x4, 0x5, 0x6 }, + .pin_cvt = { 0x2, 0x2, 0x2 }, + }, +}; + +static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) { struct intel_hdmi_spec *spec; + int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; + *spec = static_specs[spec_id]; codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops; - snd_hda_eld_proc_new(codec, &spec->sink_eld); + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); init_channel_allocations(); @@ -656,16 +765,12 @@ static int do_patch_intel_hdmi(struct hda_codec *codec) static int patch_intel_hdmi(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x03; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 0); } static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x04; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 1); } static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { -- cgit v0.10.2 From 69fb346896b4265c0cbcbd2fdd1a97ae09fe198d Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:45:04 +0100 Subject: ALSA: hda - get intelhdmi max channels from widget caps Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 08ea88d..3c68aa9 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -650,7 +650,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, static struct hda_pcm_stream intel_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 8, .ops = { .prepare = intel_hdmi_playback_pcm_prepare, .cleanup = intel_hdmi_playback_pcm_cleanup, @@ -667,11 +666,17 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec) codec->pcm_info = info; for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + info->name = intel_hdmi_pcm_names[i]; info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; } return 0; -- cgit v0.10.2 From f424367c3a393ca8b9669ceaa5b7f959d83bb14c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:45:35 +0100 Subject: ALSA: hda - auto parse intelhdmi cvt/pin configurations Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 3c68aa9..1c374f1 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -213,6 +213,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { }; +/* + * HDA/HDMI auto parsing + */ + static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) { int i; @@ -225,6 +229,113 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) return -EINVAL; } +static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, + HDA_MAX_CONNECTIONS); + if (conn_len > 1) + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + else + curr = 0; + + index = hda_node_index(spec->pin, pin_nid); + if (index < 0) + return -EINVAL; + + spec->pin_cvt[index] = conn_list[curr]; + + return 0; +} + +static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_pins >= INTEL_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d \n", pin_nid); + return -EINVAL; + } + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return intel_hdmi_read_pin_conn(codec, pin_nid); +} + +static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= INTEL_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d \n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + +static int intel_hdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (intel_hdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & AC_PINCAP_HDMI)) + continue; + if (intel_hdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + return 0; +} + /* * HDMI routines */ @@ -756,8 +867,15 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) if (spec == NULL) return -ENOMEM; - *spec = static_specs[spec_id]; codec->spec = spec; + if (intel_hdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } + if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) + snd_printk(KERN_WARNING + "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops; for (i = 0; i < spec->num_pins; i++) -- cgit v0.10.2 From fd080b2d8a6a13992b4b1b6300e1befdb9e089f2 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:46:22 +0100 Subject: ALSA: hda - remove static intelhdmi configurations Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 1c374f1..650de1b 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -841,24 +841,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static struct intel_hdmi_spec static_specs[] = { - { - .num_cvts = 1, - .num_pins = 1, - .cvt = { 0x2 }, - .pin = { 0x3 }, - .pin_cvt = { 0x2 }, - }, - { - .num_cvts = 2, - .num_pins = 3, - .cvt = { 0x2, 0x3 }, - .pin = { 0x4, 0x5, 0x6 }, - .pin_cvt = { 0x2, 0x2, 0x2 }, - }, -}; - -static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) +static int patch_intel_hdmi(struct hda_codec *codec) { struct intel_hdmi_spec *spec; int i; @@ -873,9 +856,6 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) kfree(spec); return -EINVAL; } - if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) - snd_printk(KERN_WARNING - "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops; for (i = 0; i < spec->num_pins; i++) @@ -886,23 +866,13 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) return 0; } -static int patch_intel_hdmi(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 0); -} - -static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 1); -} - static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, - { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ }; -- cgit v0.10.2 From 36dd5c4afff825fca1b6ccde678f51d6933a6850 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 20 Oct 2009 13:18:04 +0800 Subject: ALSA: VIA HDA: Add support for VT1818S. Add support for VT1818S codec, which is similiar with VT1708S. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 89e084d..5ec0e39 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -41,6 +41,7 @@ /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ /* 2009-07-08 Lydia Wang Add support for VT2002P */ /* 2009-07-21 Lydia Wang Add support for VT1812 */ +/* 2009-09-19 Lydia Wang Add support for VT1818S */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -195,6 +196,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT2002P; else if (dev_id == 0x0448) codec_type = VT1812; + else if (dev_id == 0x0440) + codec_type = VT1708S; else codec_type = UNKNOWN; return codec_type; @@ -4130,11 +4133,17 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - spec->stream_name_analog = "VT1708S Analog"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_analog = "VT1818S Analog"; + else + spec->stream_name_analog = "VT1708S Analog"; spec->stream_analog_playback = &vt1708S_pcm_analog_playback; spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - spec->stream_name_digital = "VT1708S Digital"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_digital = "VT1818S Digital"; + else + spec->stream_name_digital = "VT1708S Digital"; spec->stream_digital_playback = &vt1708S_pcm_digital_playback; if (!spec->adc_nids && spec->input_mux) { @@ -6231,6 +6240,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, + { .id = 0x11060440, .name = "VT1818S", + .patch = patch_vt1708S}, {} /* terminator */ }; -- cgit v0.10.2 From 6a5f96ce72b9e1a4bf06422df53fa819947d2293 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Oct 2009 12:31:39 +0100 Subject: ALSA: hda - Add a proper ifdef to a debug code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a proper ifdef CONFIG_SND_DEBUG_VERBOSE to avoid a compile warning: sound/pci/hda/patch_intelhdmi.c:406: warning: ‘hdmi_get_channel_count’ defined but not used Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 650de1b..4f25f08 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -403,11 +403,13 @@ static void hdmi_stop_infoframe_trans(struct hda_codec *codec, AC_DIPXMIT_DISABLE); } +#ifdef CONFIG_SND_DEBUG_VERBOSE static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } +#endif static void hdmi_set_channel_count(struct hda_codec *codec, hda_nid_t nid, int chs) -- cgit v0.10.2 From 23c4a8812a17f0af2b573a63fc991baa7d3570ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Oct 2009 13:21:49 +0100 Subject: ALSA: hda - Switch to polling mode before disabling MSI When any codec communication error happens, try to switch to the polling mode first before turning off MSI. MSI gets more stable nowadays, thus we should keep it on as much as possible. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d0effa3..a0eface 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -677,6 +677,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } + if (!chip->polling_mode) { + snd_printk(KERN_WARNING SFX "azx_get_response timeout, " + "switching to polling mode: last cmd=0x%08x\n", + chip->last_cmd[addr]); + chip->polling_mode = 1; + goto again; + } + if (chip->msi) { snd_printk(KERN_WARNING SFX "No response from codec, " "disabling MSI: last cmd=0x%08x\n", @@ -692,14 +700,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, goto again; } - if (!chip->polling_mode) { - snd_printk(KERN_WARNING SFX "azx_get_response timeout, " - "switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); - chip->polling_mode = 1; - goto again; - } - if (chip->probing) { /* If this critical timeout happens during the codec probing * phase, this is likely an access to a non-existing codec -- cgit v0.10.2 From 167eae5a17b3cd44a324dbb972c338e489413f54 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 6 Nov 2009 15:47:50 +0100 Subject: ALSA: hda - Reset pins of IDT/STAC codecs at free Some laptops cause annoying clicks or noises at shutdown/reboot since the speaker pin is set still high. Apply the same procedure used for the suspend to avoid such clicks/noises for freeing the codec, too. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8eb6508..3087705 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4327,6 +4327,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void stac92xx_shutup(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + hda_nid_t nid; + + /* reset each pin before powering down DAC/ADC to avoid click noise */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wcaps); + if (wid_type == AC_WID_PIN) + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); +} + static void stac92xx_free(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4334,6 +4356,7 @@ static void stac92xx_free(struct hda_codec *codec) if (! spec) return; + stac92xx_shutup(codec); stac92xx_free_jacks(codec); snd_array_free(&spec->events); @@ -4793,24 +4816,7 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) { - struct sigmatel_spec *spec = codec->spec; - int i; - hda_nid_t nid; - - /* reset each pin before powering down DAC/ADC to avoid click noise */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type == AC_WID_PIN) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); + stac92xx_shutup(codec); return 0; } #endif -- cgit v0.10.2 From fb8d1a344dbe963f16249d07eee8415e93f9f3c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Nov 2009 16:02:29 +0100 Subject: ALSA: hda - Add reboot notifier to each codec Add reboot notifier to each codec so that it can do some workarounds needed for reboot. So far, patch_sigmatel.c calls its shutup routine for avoiding noises at reboot on some HP machines. References: Novell bnc#544779 http://bugzilla.novell.com/show_bug.cgi?id=544779 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2c13663..146f95b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3404,6 +3404,23 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } } +/* call each reboot notifier */ +void snd_hda_bus_reboot_notify(struct hda_bus *bus) +{ + struct hda_codec *codec; + + if (!bus) + return; + list_for_each_entry(codec, &bus->codec_list, list) { +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!codec->power_on) + continue; +#endif + if (codec->patch_ops.reboot_notify) + codec->patch_ops.reboot_notify(codec); + } +} + /* * open the digital out in the exclusive mode */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 99552fb..6240608 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -674,6 +674,7 @@ struct hda_codec_ops { #ifdef CONFIG_SND_HDA_POWER_SAVE int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif + void (*reboot_notify)(struct hda_codec *codec); }; /* record for amp information cache */ @@ -910,6 +911,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, * Misc */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); +void snd_hda_bus_reboot_notify(struct hda_bus *bus); /* * power management diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 55c7da3..0d3e0c9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2150,6 +2150,7 @@ static int azx_resume(struct pci_dev *pci) static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) { struct azx *chip = container_of(nb, struct azx, reboot_notifier); + snd_hda_bus_reboot_notify(chip->bus); azx_stop_chip(chip); return NOTIFY_OK; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3087705..9c33700 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4831,6 +4831,7 @@ static struct hda_codec_ops stac92xx_patch_ops = { .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif + .reboot_notify = stac92xx_shutup, }; static int patch_stac9200(struct hda_codec *codec) -- cgit v0.10.2 From e3303235209c0496b490e10ab131e72a9568c153 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 10 Nov 2009 14:53:02 +0100 Subject: ALSA: hda - proc - show which I/O NID is associated to PCM device Output something like: Node 0x02 [Audio Output] wcaps 0x11: Stereo Device: name="ALC888 Analog", type="Audio", device=0, substream=0 Converter: stream=0, channel=0 ... Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 146f95b..480d1ec 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2877,14 +2877,15 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" +}; + /* * get the empty PCM device number to assign */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { - static const char *dev_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" - }; /* audio device indices; not linear to keep compatibility */ static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, @@ -2903,7 +2904,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) return audio_idx[type][i]; - snd_printk(KERN_WARNING "Too many %s devices\n", dev_name[type]); + snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]); return -EAGAIN; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 6240608..cbf199a 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -894,6 +894,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ +extern const char *snd_hda_pcm_type_name[]; int snd_hda_build_pcms(struct hda_bus *bus); int snd_hda_codec_build_pcms(struct hda_codec *codec); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 95f24e4..f5639c2 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -309,7 +309,21 @@ static void print_audio_io(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, unsigned int wid_type) { - int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + int pcm, conv; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + int type; + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + for (type = 0; type < 2; type++) { + if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) + continue; + snd_iprintf(buffer, " Device: name=\"%s\", type=\"%s\", device=%i, substream=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device, + cpcm->pcm->streams[type].substream->number); + } + } + conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); snd_iprintf(buffer, " Converter: stream=%d, channel=%d\n", (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, -- cgit v0.10.2 From 8f217a226cfa7b960b8a6c00cef6b4de2c5dd030 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Nov 2009 18:26:12 +0100 Subject: ALSA: hda - Add missing export for snd_hda_bus_reboot_notify ... forgot to add for modules. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 480d1ec..2b787b0 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3421,6 +3421,7 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) codec->patch_ops.reboot_notify(codec); } } +EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); /* * open the digital out in the exclusive mode -- cgit v0.10.2 From a2f6309e8392e2c14c04594fca8b4876c8c9bc36 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Nov 2009 09:34:25 +0100 Subject: ALSA: hda - Add power on/off counter Added the power on/off counter and expose via sysfs files. The sysfs files, power_on_acct and power_off_acct, are created under each codec hwdep sysfs directory (e.g. /sys/class/sound/hwC0D0). The files show the msec length of the codec power-on and power-off, respectively. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2b787b0..444d903 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -515,6 +515,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { snd_hda_hwdep_add_sysfs(codec); + snd_hda_hwdep_add_power_sysfs(codec); } return 0; } @@ -2452,9 +2453,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); #ifdef CONFIG_SND_HDA_POWER_SAVE + snd_hda_update_power_acct(codec); cancel_delayed_work(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; + codec->power_jiffies = jiffies; #endif } @@ -3191,6 +3194,17 @@ static void hda_keep_power_on(struct hda_codec *codec) { codec->power_count++; codec->power_on = 1; + codec->power_jiffies = jiffies; +} + +void snd_hda_update_power_acct(struct hda_codec *codec) +{ + unsigned long delta = jiffies - codec->power_jiffies; + if (codec->power_on) + codec->power_on_acct += delta; + else + codec->power_off_acct += delta; + codec->power_jiffies += delta; } void snd_hda_power_up(struct hda_codec *codec) @@ -3201,7 +3215,9 @@ void snd_hda_power_up(struct hda_codec *codec) if (codec->power_on || codec->power_transition) return; + snd_hda_update_power_acct(codec); codec->power_on = 1; + codec->power_jiffies = jiffies; if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cbf199a..b16678c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -812,6 +812,9 @@ struct hda_codec { unsigned int power_transition :1; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ + unsigned long power_on_acct; + unsigned long power_off_acct; + unsigned long power_jiffies; #endif /* codec-specific additional proc output */ @@ -936,6 +939,7 @@ const char *snd_hda_get_jack_location(u32 cfg); void snd_hda_power_up(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec); #define snd_hda_codec_needs_resume(codec) codec->power_count +void snd_hda_update_power_acct(struct hda_codec *codec); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index cc24e67..d243286 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static ssize_t power_on_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); +} + +static ssize_t power_off_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); +} + +static struct device_attribute power_attrs[] = { + __ATTR_RO(power_on_acct), + __ATTR_RO(power_off_acct), +}; + +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + struct snd_hwdep *hwdep = codec->hwdep; + int i; + + for (i = 0; i < ARRAY_SIZE(power_attrs); i++) + snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, + hwdep->device, &power_attrs[i]); + return 0; +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + #ifdef CONFIG_SND_HDA_RECONFIG /* diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 461e0c1..015fbac 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -437,6 +437,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif +#ifdef CONFIG_SND_HDA_POWER_SAVE +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); +#else +static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + return 0; +} +#endif + #ifdef CONFIG_SND_HDA_RECONFIG int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); #else -- cgit v0.10.2 From f8b7163529831ee3ad6a1aeaa0f1256cb527008d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Nov 2009 09:50:28 +0100 Subject: ALSA: hda - Don't access invalid substream in proc file The commit e3303235209c0496b490e10ab131e72a9568c153 "ALSA: hda - proc - show which I/O NID is associated to PCM device" introduces the access to substream pointer. But, PCMs may have no substreams in one or both directions, and this results in NULL dereference. Also, print the first substream number doesn't make sense. This patch removes the access to the substream pointer, and reformat to fit to the standard coding style. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f5639c2..f5b783c 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -316,11 +316,11 @@ static void print_audio_io(struct snd_info_buffer *buffer, for (type = 0; type < 2; type++) { if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) continue; - snd_iprintf(buffer, " Device: name=\"%s\", type=\"%s\", device=%i, substream=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device, - cpcm->pcm->streams[type].substream->number); + snd_iprintf(buffer, " Device: name=\"%s\", " + "type=\"%s\", device=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device); } } conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); -- cgit v0.10.2 From 7288561af9a931c59e431336b553d897ee37b67d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Nov 2009 10:01:18 +0100 Subject: ALSA: hda - Fix build error without CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_POWER_SAVE is independent from CONFIG_SND_HDA_HWDEP. Thus snd_hda_hwdep_add_power_sysfs() needs the check of both kconfigs. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 015fbac..c1ca318 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -437,7 +437,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP) int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); #else static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) -- cgit v0.10.2 From 01a1796bc52f625edc23bf995d200e1556eec544 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Fri, 13 Nov 2009 16:47:10 -0800 Subject: sound/pci/hda/patch_via.c: work around gcc-4.0.2 ICE sound/pci/hda/patch_via.c: In function 'via_hp_bind_automute': sound/pci/hda/patch_via.c:2074: internal compiler error: in do_SUBST, at combine.c:462 Please submit a full bug report, with preprocessed source if appropriate. See for instructions. [added a comment by tiwai] Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5ec0e39..5a85600 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2043,7 +2043,10 @@ static void via_speaker_automute(struct hda_codec *codec) /* mute line-out and internal speaker if HP is plugged */ static void via_hp_bind_automute(struct hda_codec *codec) { - unsigned int hp_present, present = 0; + /* use long instead of int below just to avoid an internal compiler + * error with gcc 4.0.x + */ + unsigned long hp_present, present = 0; struct via_spec *spec = codec->spec; int i; -- cgit v0.10.2 From 123c07aeddd71fbb295842a8c19866e780b9a100 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 21 Oct 2009 14:48:23 +0200 Subject: ALSA: hda_intel: Digital PC Beep - change behaviour for input layer Original implementation was keeping registered input device for SND_BEEP and SND_TONE events all time. This patch changes this behaviour: If digital PC Beep is turned off using universal control switch, the input device is unregistered. Explanation: The kd_mksound() send SND_BEEP and SND_TONE only to last registered device acceping those events. It means that the HDA Intel audio driver blocks also the internal PC Speaker device (pcspkr.c driver) even if the HDA Beep is muted. The user can easy disable all beeps using 'setterm -blength 0' or 'xset b off' command. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 3f51a98..0e98653 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, return 0; } -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +static void snd_hda_do_detach(struct hda_beep *beep) +{ + input_unregister_device(beep->dev); + beep->dev = NULL; + cancel_work_sync(&beep->beep_work); + /* turn off beep for sure */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); +} + +static int snd_hda_do_attach(struct hda_beep *beep) { struct input_dev *input_dev; - struct hda_beep *beep; + struct hda_codec *codec = beep->codec; int err; - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly */ - - beep = kzalloc(sizeof(*beep), GFP_KERNEL); - if (beep == NULL) - return -ENOMEM; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); input_dev = input_allocate_device(); if (!input_dev) { - kfree(beep); + printk(KERN_INFO "hda_beep: unable to allocate input device\n"); return -ENOMEM; } @@ -151,21 +153,71 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) err = input_register_device(input_dev); if (err < 0) { input_free_device(input_dev); - kfree(beep); + printk(KERN_INFO "hda_beep: unable to register input device\n"); return err; } + beep->dev = input_dev; + return 0; +} + +static void snd_hda_do_register(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, register_work); + int request; + + mutex_lock(&beep->mutex); + request = beep->request_enable; + if (beep->enabled != request) { + if (!request) { + snd_hda_do_detach(beep); + } else { + if (snd_hda_do_attach(beep) < 0) + goto __out; + } + beep->enabled = request; + } + __out: + mutex_unlock(&beep->mutex); +} + +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) +{ + struct hda_beep *beep = codec->beep; + enable = !!enable; + if (beep && beep->enabled != enable) { + beep->request_enable = enable; + schedule_work(&beep->register_work); + return 1; + } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); + +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +{ + struct hda_beep *beep; + + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly */ + beep = kzalloc(sizeof(*beep), GFP_KERNEL); + if (beep == NULL) + return -ENOMEM; + snprintf(beep->phys, sizeof(beep->phys), + "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); beep->nid = nid; - beep->dev = input_dev; beep->codec = codec; - beep->enabled = 1; codec->beep = beep; + INIT_WORK(&beep->register_work, &snd_hda_do_register); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); + mutex_init(&beep->mutex); + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); @@ -174,11 +226,11 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; if (beep) { - cancel_work_sync(&beep->beep_work); - - input_unregister_device(beep->dev); - kfree(beep); + cancel_work_sync(&beep->register_work); + if (beep->enabled) + snd_hda_do_detach(beep); codec->beep = NULL; + kfree(beep); } } EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 0c3de78..68465f6 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -32,11 +32,15 @@ struct hda_beep { int tone; hda_nid_t nid; unsigned int enabled:1; + unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ + struct work_struct register_work; /* scheduled task for beep event */ struct work_struct beep_work; /* scheduled task for beep event */ + struct mutex mutex; }; #ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); void snd_hda_detach_beep_device(struct hda_codec *codec); #else diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 444d903..7fd2abe 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -30,6 +30,7 @@ #include #include #include "hda_local.h" +#include "hda_beep.h" #include /* @@ -1734,6 +1735,17 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + + snd_hda_enable_beep_device(codec, *valp); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); + /* * bound volume controls * diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c1ca318..3001794 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -66,6 +66,19 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put_beep, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +/* special beep mono mute switch */ +#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* special beep stereo mute switch */ +#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); @@ -81,6 +94,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d603f6..a029361 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -159,7 +159,7 @@ static void ad198x_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 49de107..8c04e0e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2413,7 +2413,7 @@ static void alc_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8d65d2b..87ba239 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2648,6 +2648,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MUTE_BEEP, STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, @@ -2658,6 +2659,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), STAC_MONO_MUX, STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), @@ -3221,11 +3223,14 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err; + int err, type = STAC_CTL_WIDGET_MUTE_BEEP; + + if (spec->anabeep_nid == nid) + type = STAC_CTL_WIDGET_MUTE; /* check for mute support for the the amp */ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + err = stac92xx_add_control(spec, type, "Beep Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) @@ -3258,12 +3263,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int enabled = !!ucontrol->value.integer.value[0]; - if (codec->beep->enabled != enabled) { - codec->beep->enabled = enabled; - return 1; - } - return 0; + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); } static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { -- cgit v0.10.2 From 13dab0808bb41b18888e1758a060a685deee1f30 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 3 Nov 2009 14:29:50 +0100 Subject: ALSA: hda_intel: Digital PC Beep - delay input device unregistration The massive register/unregister calls for input device layer might be overkill. Delay unregister call by one HZ as workaround. Also, as benefit, beep->enabled variable is changed immediately now (not from workqueue). Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 0e98653..74db40e 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -164,20 +164,21 @@ static void snd_hda_do_register(struct work_struct *work) { struct hda_beep *beep = container_of(work, struct hda_beep, register_work); - int request; mutex_lock(&beep->mutex); - request = beep->request_enable; - if (beep->enabled != request) { - if (!request) { - snd_hda_do_detach(beep); - } else { - if (snd_hda_do_attach(beep) < 0) - goto __out; - } - beep->enabled = request; - } - __out: + if (beep->enabled && !beep->dev) + snd_hda_do_attach(beep); + mutex_unlock(&beep->mutex); +} + +static void snd_hda_do_unregister(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, unregister_work.work); + + mutex_lock(&beep->mutex); + if (!beep->enabled && beep->dev) + snd_hda_do_detach(beep); mutex_unlock(&beep->mutex); } @@ -185,9 +186,19 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) { struct hda_beep *beep = codec->beep; enable = !!enable; - if (beep && beep->enabled != enable) { - beep->request_enable = enable; - schedule_work(&beep->register_work); + if (beep == NULL) + return 0; + if (beep->enabled != enable) { + beep->enabled = enable; + if (enable) { + cancel_delayed_work(&beep->unregister_work); + schedule_work(&beep->register_work); + } else { + /* turn off beep */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); + schedule_delayed_work(&beep->unregister_work, HZ); + } return 1; } return 0; @@ -215,6 +226,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) codec->beep = beep; INIT_WORK(&beep->register_work, &snd_hda_do_register); + INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); mutex_init(&beep->mutex); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 68465f6..53eba8d 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -34,7 +34,8 @@ struct hda_beep { unsigned int enabled:1; unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ - struct work_struct register_work; /* scheduled task for beep event */ + struct work_struct register_work; /* registration work */ + struct delayed_work unregister_work; /* unregistration work */ struct work_struct beep_work; /* scheduled task for beep event */ struct mutex mutex; }; -- cgit v0.10.2 From 5f81669750504b1e7e00acde5068d972af466f29 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 4 Nov 2009 12:46:49 +0100 Subject: ALSA: hda: beep - add missing cancel_delayed_work The unregister work should be also canceled in snd_hda_detach_beep_device() function. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 74db40e..c819152 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -239,6 +239,7 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) struct hda_beep *beep = codec->beep; if (beep) { cancel_work_sync(&beep->register_work); + cancel_delayed_work(&beep->unregister_work); if (beep->enabled) snd_hda_do_detach(beep); codec->beep = NULL; -- cgit v0.10.2 From 2dca0bba70ce3c233be152e384580c134935332d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 13 Nov 2009 18:41:52 +0100 Subject: ALSA: hda - add beep_mode module parameter The beep_mode parameter for snd-hda-intel module allows to choose among different digital beep device registation to the input layer. 0 = do not register to the input layer 1 = register to the input layer all time 2 = use "Beep Switch" control exported to user space mixer applications Also, introduce CONFIG_SND_HDA_INPUT_BEEP_MODE for default value. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 55545e0..25ae10e 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -38,6 +38,17 @@ config SND_HDA_INPUT_BEEP Say Y here to build a digital beep interface for HD-audio driver. This interface is used to generate digital beeps. +config SND_HDA_INPUT_BEEP_MODE + int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)" + depends on SND_HDA_INPUT_BEEP=y + default "1" + range 0 2 + help + Set 0 to disable the digital beep interface for HD-audio by default. + Set 1 to always enable the digital beep interface for HD-audio by + default. Set 2 to control the beep device registration to input + layer using a "Beep Switch" in mixer applications. + config SND_HDA_INPUT_JACK bool "Support jack plugging notification via input layer" depends on INPUT=y || INPUT=SND_HDA_INTEL diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index c819152..9e48798 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -190,14 +190,19 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) return 0; if (beep->enabled != enable) { beep->enabled = enable; - if (enable) { - cancel_delayed_work(&beep->unregister_work); - schedule_work(&beep->register_work); - } else { + if (!enable) { /* turn off beep */ snd_hda_codec_write_cache(beep->codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, 0); - schedule_delayed_work(&beep->unregister_work, HZ); + } + if (beep->mode == HDA_BEEP_MODE_SWREG) { + if (enable) { + cancel_delayed_work(&beep->unregister_work); + schedule_work(&beep->register_work); + } else { + schedule_delayed_work(&beep->unregister_work, + HZ); + } } return 1; } @@ -223,6 +228,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) beep->nid = nid; beep->codec = codec; + beep->mode = codec->beep_mode; codec->beep = beep; INIT_WORK(&beep->register_work, &snd_hda_do_register); @@ -230,6 +236,11 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); mutex_init(&beep->mutex); + if (beep->mode == HDA_BEEP_MODE_ON) { + beep->enabled = 1; + snd_hda_do_register(&beep->register_work); + } + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 53eba8d..17dd1c3 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -24,10 +24,15 @@ #include "hda_codec.h" +#define HDA_BEEP_MODE_ON 0 +#define HDA_BEEP_MODE_OFF 1 +#define HDA_BEEP_MODE_SWREG 2 + /* beep information */ struct hda_beep { struct input_dev *dev; struct hda_codec *codec; + unsigned int mode; char phys[32]; int tone; hda_nid_t nid; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b16678c..5192056 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -772,6 +772,7 @@ struct hda_codec { /* beep device */ struct hda_beep *beep; + unsigned int beep_mode; /* widget capabilities cache */ unsigned int num_nodes; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e73e395..91bcbda 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -64,6 +64,10 @@ static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = + CONFIG_SND_HDA_INPUT_BEEP_MODE}; +#endif module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); module_param_array(patch, charp, NULL, 0444); MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +module_param_array(beep_mode, int, NULL, 0444); +MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " + "(0=off, 1=on, 2=mute switch on/off) (default=1)."); +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; @@ -404,6 +413,7 @@ struct azx { unsigned short codec_mask; int codec_probe_mask; /* copied from probe_mask option */ struct hda_bus *bus; + unsigned int beep_mode; /* CORB/RIRB */ struct azx_rb corb; @@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) continue; + codec->beep_mode = chip->beep_mode; codecs++; } } @@ -2579,6 +2590,10 @@ static int __devinit azx_probe(struct pci_dev *pci, goto out_free; card->private_data = chip; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + chip->beep_mode = beep_mode[dev]; +#endif + /* create codec instances */ err = azx_codec_create(chip, model[dev]); if (err < 0) -- cgit v0.10.2 From 3911a4c19e927738766003839aa447becbdbaa27 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 11 Nov 2009 13:43:01 +0100 Subject: ALSA: hda - proc - introduce Control: lines to show mixer<->NID assignment This is an initial patch to show universal control<->NID assigment in proc codec file. The change helps to debug codec related problems. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7fd2abe..1ed1d88 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -946,7 +946,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1517,18 +1517,20 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); /* Add a control element and assign to the codec */ -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl) +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl) { int err; - struct snd_kcontrol **knewp; + struct hda_nid_item *item; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; - knewp = snd_array_new(&codec->mixers); - if (!knewp) + item = snd_array_new(&codec->mixers); + if (!item) return -ENOMEM; - *knewp = kctl; + item->kctl = kctl; + item->nid = nid; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); @@ -1537,9 +1539,9 @@ EXPORT_SYMBOL_HDA(snd_hda_ctl_add); void snd_hda_ctls_clear(struct hda_codec *codec) { int i; - struct snd_kcontrol **kctls = codec->mixers.list; + struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, kctls[i]); + snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); } @@ -1645,7 +1647,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, kctl = snd_ctl_make_virtual_master(name, tlv); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; @@ -2139,7 +2141,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return -ENOMEM; kctl->id.index = idx; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -2184,8 +2186,8 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, if (!mout->dig_out_nid) return 0; /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, - snd_ctl_new1(&spdif_share_sw, mout)); + return snd_hda_ctl_add(codec, mout->dig_out_nid, + snd_ctl_new1(&spdif_share_sw, mout)); } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); @@ -2289,7 +2291,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) if (!kctl) return -ENOMEM; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -3165,7 +3167,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) { if (!codec->addr) return err; @@ -3173,7 +3175,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) if (!kctl) return -ENOMEM; kctl->id.device = codec->addr; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b36f6c5..092c6a7 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_INPUT, index); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec)); + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec)); if (err < 0) return err; @@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec) HDA_CODEC_VOLUME(name, adc_node->nid, spec->input_mux.items[i].index, HDA_INPUT); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, adc_node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3001794..e6a0918 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -440,7 +440,13 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl); +struct hda_nid_item { + struct snd_kcontrol *kctl; + hda_nid_t nid; +}; + +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl); void snd_hda_ctls_clear(struct hda_codec *codec); /* @@ -514,7 +520,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, * AMP control callbacks */ /* retrieve parameters from private_value */ -#define get_amp_nid(kc) ((kc)->private_value & 0xffff) +#define get_amp_nid_(pv) ((pv) & 0xffff) +#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f5b783c..f465cff 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -46,6 +46,41 @@ static const char *get_wid_type_name(unsigned int wid_value) return "UNKNOWN Widget"; } +static void print_nid_mixers(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int i; + struct hda_nid_item *items = codec->mixers.list; + struct snd_kcontrol *kctl; + for (i = 0; i < codec->mixers.used; i++) { + if (items[i].nid == nid) { + kctl = items[i].kctl; + snd_iprintf(buffer, + " Control: name=\"%s\", index=%i, device=%i\n", + kctl->id.name, kctl->id.index, kctl->id.device); + } + } +} + +static void print_nid_pcms(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int pcm, type; + struct hda_pcm *cpcm; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + cpcm = &codec->pcm_info[pcm]; + for (type = 0; type < 2; type++) { + if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) + continue; + snd_iprintf(buffer, " Device: name=\"%s\", " + "type=\"%s\", device=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device); + } + } +} + static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { @@ -309,21 +344,7 @@ static void print_audio_io(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, unsigned int wid_type) { - int pcm, conv; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - int type; - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - for (type = 0; type < 2; type++) { - if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) - continue; - snd_iprintf(buffer, " Device: name=\"%s\", " - "type=\"%s\", device=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device); - } - } - conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); snd_iprintf(buffer, " Converter: stream=%d, channel=%d\n", (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, @@ -471,6 +492,7 @@ static void print_gpio(struct snd_info_buffer *buffer, (data & (1<private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), + kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index d08353d..af47801 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, @@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } #define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 8ba3068..9ac09e4 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, *kctlp); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static int add_volume(struct hda_codec *codec, const char *name, @@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, *kctlp); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) @@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) spec->vmaster_sw = snd_ctl_make_virtual_master("Master Playback Switch", NULL); - err = snd_hda_ctl_add(codec, spec->vmaster_sw); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); if (err < 0) return err; snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); spec->vmaster_vol = snd_ctl_make_virtual_master("Master Playback Volume", tlv); - err = snd_hda_ctl_add(codec, spec->vmaster_vol); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); if (err < 0) return err; return 0; @@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } if (spec->num_inputs > 1 && !spec->mic_detect) { - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cs_capture_source, codec)); if (err < 0) return err; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8c04e0e..fff9de2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2461,7 +2461,8 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 87ba239..a3872b9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1085,7 +1085,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (!spec->auto_mic && spec->num_dmuxes > 0 && snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { stac_dmux_mixer.count = spec->num_dmuxes; - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_dmux_mixer, codec)); if (err < 0) return err; @@ -1101,7 +1101,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) spec->spdif_mute = 1; } stac_smux_mixer.count = spec->num_smuxes; - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_smux_mixer, codec)); if (err < 0) return err; -- cgit v0.10.2 From 4d02d1b638af580ae3d69367248539a8b3893064 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 12 Nov 2009 10:15:48 +0100 Subject: ALSA: hda - proc - add support for dynamic controls to mixer<->NID mapping This patch adds support for dynamically created controls to proc codec file (Control: lines). Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 1ed1d88..d71e651 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1523,6 +1523,11 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_nid_item *item; + if (kctl->id.subdevice & (1<<31)) { + if (nid == 0) + nid = kctl->id.subdevice & 0xffff; + kctl->id.subdevice = 0; + } err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; @@ -3160,7 +3165,7 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); */ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) { - int err; + int err; for (; knew->name; knew++) { struct snd_kcontrol *kctl; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index e6a0918..3bfcf42 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -33,6 +33,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -53,6 +54,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -69,6 +71,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index ef33839..2d34560 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2571,6 +2571,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fff9de2..c0a98e7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4323,6 +4323,8 @@ static int add_control(struct alc_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a3872b9..d2ddb95 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2671,7 +2671,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { static struct snd_kcontrol_new * stac_control_new(struct sigmatel_spec *spec, struct snd_kcontrol_new *ktemp, - const char *name) + const char *name, + hda_nid_t nid) { struct snd_kcontrol_new *knew; @@ -2687,6 +2688,8 @@ stac_control_new(struct sigmatel_spec *spec, spec->kctls.alloced--; return NULL; } + if (nid) + knew->subdevice = (1<<31)|nid; return knew; } @@ -2695,7 +2698,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec, int idx, const char *name, unsigned long val) { - struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name); + struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, + get_amp_nid_(val)); if (!knew) return -ENOMEM; knew->index = idx; @@ -2766,7 +2770,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) if (!spec->num_adcs || imux->num_items <= 1) return 0; /* no need for input source control */ knew = stac_control_new(spec, &stac_input_src_temp, - stac_input_src_temp.name); + stac_input_src_temp.name, 0); if (!knew) return -ENOMEM; knew->count = spec->num_adcs; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5a85600..14219d8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -442,6 +442,8 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } -- cgit v0.10.2 From 9c96fa599fe4f0ccc6e3e606df6652335afe28e8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 11:25:33 +0100 Subject: ALSA: hda - Get rid of magic digits for subdev hack Define a proper const for a magic 31bit flag for subdev / NID setup with a brief comment. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d71e651..5e21b35 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1523,7 +1523,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_nid_item *item; - if (kctl->id.subdevice & (1<<31)) { + if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { if (nid == 0) nid = kctl->id.subdevice & 0xffff; kctl->id.subdevice = 0; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3bfcf42..4e77f47 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -23,6 +23,15 @@ #ifndef __SOUND_HDA_LOCAL_H #define __SOUND_HDA_LOCAL_H +/* We abuse kcontrol_new.subdev field to pass the NID corresponding to + * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, + * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. + * + * Note that the subdevice field is cleared again before the real registration + * in snd_hda_ctl_add(), so that this value won't appear in the outside. + */ +#define HDA_SUBDEV_NID_FLAG (1U << 31) + /* * for mixer controls */ @@ -33,7 +42,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -54,7 +63,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -71,7 +80,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d34560..ceb0c60 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2572,7 +2572,7 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, if (! knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c0a98e7..eee3143 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4324,7 +4324,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d2ddb95..7f76a97 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2689,7 +2689,7 @@ stac_control_new(struct sigmatel_spec *spec, return NULL; } if (nid) - knew->subdevice = (1<<31)|nid; + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; return knew; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 14219d8..0c621d7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -443,7 +443,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } -- cgit v0.10.2 From d019361a0802924e2dbcd7313fa3dcfc960222a7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 12:03:49 +0100 Subject: ALSA: hda - Add description of beep_mode in ALSA-Configuration.txt Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 780c213..d29de1c 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -798,6 +798,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. setup before initializing the codecs. This option is available only when CONFIG_SND_HDA_PATCH_LOADER=y is set. See HD-Audio.txt for details. + beep_mode - Selects the beep registration mode (0=off, 1=on, 2= + dynamic registration via mute switch on/off); the default + value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig. [Single (global) options] single_cmd - Use single immediate commands to communicate with -- cgit v0.10.2 From 85dd662ff4d2967084acfc761a33717383297e42 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 11 Nov 2009 13:49:07 +0100 Subject: ALSA: hda - move snd_hda_pcm_type_name from hda_codec.h to hda_local.h The snd_hda_pcm_type_name array is local only. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 5192056..be6c5f4 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -898,7 +898,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ -extern const char *snd_hda_pcm_type_name[]; int snd_hda_build_pcms(struct hda_bus *bus); int snd_hda_codec_build_pcms(struct hda_codec *codec); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4e77f47..7c04983 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -92,6 +92,8 @@ #define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) +extern const char *snd_hda_pcm_type_name[]; + int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From d5191e50b251594bdde10d4839a952ff1646ef62 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 14:58:17 +0100 Subject: ALSA: hda - Update / add kerneldoc comments to exported functions Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5e21b35..e344235 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -94,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec); static inline void hda_keep_power_on(struct hda_codec *codec) {} #endif +/** + * snd_hda_get_jack_location - Give a location string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack location, e.g. "Rear", "Front", etc. + */ const char *snd_hda_get_jack_location(u32 cfg) { static char *bases[7] = { @@ -121,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); +/** + * snd_hda_get_jack_connectivity - Give a connectivity string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack connectivity, i.e. external or internal connection. + */ const char *snd_hda_get_jack_connectivity(u32 cfg) { static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; @@ -129,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); +/** + * snd_hda_get_jack_type - Give a type string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack type, i.e. the purpose of the jack, such as Line-Out or CD. + */ const char *snd_hda_get_jack_type(u32 cfg) { static char *jack_types[16] = { @@ -822,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, return 0; } +/** + * snd_hda_codec_set_pincfg - Override a pin default configuration + * @codec: the HDA codec + * @nid: NID to set the pin config + * @cfg: the pin default config value + * + * Override a pin default configuration value in the cache. + * This value can be read by snd_hda_codec_get_pincfg() in a higher + * priority than the real hardware value. + */ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { @@ -829,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); -/* get the current pin config value of the given pin NID */ +/** + * snd_hda_codec_get_pincfg - Obtain a pin-default configuration + * @codec: the HDA codec + * @nid: NID to get the pin config + * + * Get the current pin config value of the given pin NID. + * If the pincfg value is cached or overridden via sysfs or driver, + * returns the cached value. + */ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; @@ -1028,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr } EXPORT_SYMBOL_HDA(snd_hda_codec_new); +/** + * snd_hda_codec_configure - (Re-)configure the HD-audio codec + * @codec: the HDA codec + * + * Start parsing of the given codec tree and (re-)initialize the whole + * patch instance. + * + * Returns 0 if successful or a negative error code. + */ int snd_hda_codec_configure(struct hda_codec *codec) { int err; @@ -1090,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); +/** + * snd_hda_codec_cleanup_stream - clean up the codec for closing + * @codec: the CODEC to clean up + * @nid: the NID to clean up + */ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) { if (!nid) @@ -1165,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } -/* - * query AMP capabilities for the given widget and direction +/** + * query_amp_caps - query AMP capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * + * Query AMP capabilities for the given widget and direction. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { @@ -1189,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) } EXPORT_SYMBOL_HDA(query_amp_caps); +/** + * snd_hda_override_amp_caps - Override the AMP capabilities + * @codec: the CODEC to clean up + * @nid: the NID to clean up + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * @caps: the capability bits to set + * + * Override the cached AMP caps bits value by the given one. + * This function is useful if the driver needs to adjust the AMP ranges, + * e.g. limit to 0dB, etc. + * + * Returns zero if successful or a negative error code. + */ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { @@ -1224,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), @@ -1271,8 +1357,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, info->vol[ch] = val; } -/* - * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. +/** + * snd_hda_codec_amp_read - Read AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * + * 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) @@ -1285,8 +1378,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -/* - * update the AMP value, mask = bit mask to set, val = the value +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. */ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) @@ -1305,8 +1408,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); -/* - * update the AMP stereo with the same mask and value +/** + * snd_hda_codec_amp_stereo - update the AMP stereo values + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP values like snd_hda_codec_amp_update(), but for a + * stereo widget with the same mask and value. */ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int direction, int idx, int mask, int val) @@ -1320,7 +1432,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); #ifdef SND_HDA_NEEDS_RESUME -/* resume the all amp commands from the cache */ +/** + * snd_hda_codec_resume_amp - Resume all AMP commands from the cache + * @codec: HD-audio codec + * + * Resume the all amp commands from the cache. + */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { struct hda_amp_info *buffer = codec->amp_cache.buf.list; @@ -1346,7 +1463,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ -/* volume */ +/** + * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1402,6 +1524,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, HDA_AMP_VOLMASK, val); } +/** + * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1421,6 +1549,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); +/** + * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1445,6 +1579,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); +/** + * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) { @@ -1474,8 +1614,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); -/* - * set (static) TLV for virtual master volume; recalculated as max 0dB +/** + * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control + * @codec: HD-audio codec + * @nid: NID of a reference widget + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @tlv: TLV data to be stored, at least 4 elements + * + * Set (static) TLV data for a virtual master volume using the AMP caps + * obtained from the reference NID. + * The volume range is recalculated as if the max volume is 0dB. */ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv) @@ -1509,6 +1657,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec, return snd_ctl_find_id(codec->bus->card, &id); } +/** + * snd_hda_find_mixer_ctl - Find a mixer control element with the given name + * @codec: HD-audio codec + * @name: ctl id name string + * + * Get the control element with the given id string and IFACE_MIXER. + */ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name) { @@ -1516,7 +1671,24 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); -/* Add a control element and assign to the codec */ +/** + * snd_hda_ctl-add - Add a control element and assign to the codec + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * + * Add the given control element to an array inside the codec instance. + * All control elements belonging to a codec are supposed to be added + * by this function so that a proper clean-up works at the free or + * reconfiguration time. + * + * If non-zero @nid is passed, the NID is assigned to the control element. + * The assignment is shown in the codec proc file. + * + * snd_hda_ctl_add() checks the control subdev id field whether + * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower + * bits value is taken as the NID to assign. + */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { @@ -1540,7 +1712,10 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); -/* Clear all controls assigned to the given codec */ +/** + * snd_hda_ctls_clear - Clear all controls assigned to the given codec + * @codec: HD-audio codec + */ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; @@ -1572,6 +1747,16 @@ static void hda_unlock_devices(struct snd_card *card) spin_unlock(&card->files_lock); } +/** + * snd_hda_codec_reset - Clear all objects assigned to the codec + * @codec: HD-audio codec + * + * This frees the all PCM and control elements assigned to the codec, and + * clears the caches and restores the pin default configurations. + * + * When a device is being used, it returns -EBSY. If successfully freed, + * returns zero. + */ int snd_hda_codec_reset(struct hda_codec *codec) { struct snd_card *card = codec->bus->card; @@ -1635,7 +1820,22 @@ int snd_hda_codec_reset(struct hda_codec *codec) return 0; } -/* create a virtual master control and add slaves */ +/** + * snd_hda_add_vmaster - create a virtual master control and add slaves + * @codec: HD-audio codec + * @name: vmaster control name + * @tlv: TLV data (optional) + * @slaves: slave control names (optional) + * + * Create a virtual master control with the given name. The TLV data + * must be either NULL or a valid data. + * + * @slaves is a NULL-terminated array of strings, each of which is a + * slave control name. All controls with these names are assigned to + * the new virtual master control. + * + * This function returns zero if successful or a negative error code. + */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves) { @@ -1677,7 +1877,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, } EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); -/* switch */ +/** + * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1691,6 +1896,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); +/** + * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1711,6 +1922,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); +/** + * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1742,6 +1959,12 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +/** + * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch + * + * This function calls snd_hda_enable_beep_device(), which behaves differently + * depending on beep_mode option. + */ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1762,6 +1985,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) +/** + * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1779,6 +2008,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); +/** + * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1803,8 +2038,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); -/* - * generic bound volume/swtich controls +/** + * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. */ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1823,6 +2061,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); +/** + * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1840,6 +2084,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); +/** + * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1863,6 +2113,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); +/** + * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() macro. + */ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { @@ -2185,6 +2441,11 @@ static struct snd_kcontrol_new spdif_share_sw = { .put = spdif_share_sw_put, }; +/** + * snd_hda_create_spdif_share_sw - create Default PCM switch + * @codec: the HDA codec + * @mout: multi-out instance + */ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { @@ -2352,7 +2613,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); -/* resume the all commands from the cache */ +/** + * snd_hda_codec_resume_cache - Resume the all commands from the cache + * @codec: HD-audio codec + * + * Execute all verbs recorded in the command caches to resume. + */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { struct hda_cache_head *buffer = codec->cmd_cache.buf.list; @@ -2778,8 +3044,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, } /** - * snd_hda_is_supported_format - check whether the given node supports - * the format val + * snd_hda_is_supported_format - Check the validity of the format + * @codec: HD-audio codec + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. * * Returns 1 if supported, 0 if not. */ @@ -2899,6 +3169,7 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +/* global */ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { "Audio", "SPDIF", "HDMI", "Modem" }; @@ -3216,6 +3487,7 @@ static void hda_keep_power_on(struct hda_codec *codec) codec->power_jiffies = jiffies; } +/* update the power on/off account with the current jiffies */ void snd_hda_update_power_acct(struct hda_codec *codec) { unsigned long delta = jiffies - codec->power_jiffies; @@ -3226,6 +3498,13 @@ void snd_hda_update_power_acct(struct hda_codec *codec) codec->power_jiffies += delta; } +/** + * snd_hda_power_up - Power-up the codec + * @codec: HD-audio codec + * + * Increment the power-up counter and power up the hardware really when + * not turned on yet. + */ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; @@ -3248,9 +3527,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); #define power_save(codec) \ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) -#define power_save(codec) \ - ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) - +/** + * snd_hda_power_down - Power-down the codec + * @codec: HD-audio codec + * + * Decrement the power-up counter and schedules the power-off work if + * the counter rearches to zero. + */ void snd_hda_power_down(struct hda_codec *codec) { --codec->power_count; @@ -3264,6 +3547,19 @@ void snd_hda_power_down(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_power_down); +/** + * snd_hda_check_amp_list_power - Check the amp list and update the power + * @codec: HD-audio codec + * @check: the object containing an AMP list and the status + * @nid: NID to check / update + * + * Check whether the given NID is in the amp list. If it's in the list, + * check the current AMP status, and update the the power-status according + * to the mute status. + * + * This function is supposed to be set or called from the check_power_status + * patch ops. + */ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid) @@ -3305,6 +3601,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); /* * Channel mode helper */ + +/** + * snd_hda_ch_mode_info - Info callback helper for the channel mode enum + */ int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, const struct hda_channel_mode *chmode, @@ -3321,6 +3621,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); +/** + * snd_hda_ch_mode_get - Get callback helper for the channel mode enum + */ int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3339,6 +3642,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); +/** + * snd_hda_ch_mode_put - Put callback helper for the channel mode enum + */ int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3363,6 +3669,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); /* * input MUX helper */ + +/** + * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum + */ int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo) { @@ -3381,6 +3691,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, } EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); +/** + * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum + */ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, @@ -3440,7 +3753,10 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } } -/* call each reboot notifier */ +/** + * snd_hda_bus_reboot_notify - call the reboot notifier of each codec + * @bus: HD-audio bus + */ void snd_hda_bus_reboot_notify(struct hda_bus *bus) { struct hda_codec *codec; @@ -3458,8 +3774,8 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) } EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); -/* - * open the digital out in the exclusive mode +/** + * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode */ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3474,6 +3790,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); +/** + * snd_hda_multi_out_dig_prepare - prepare the digital out stream + */ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, @@ -3487,6 +3806,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); +/** + * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream + */ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) { @@ -3497,8 +3819,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); -/* - * release the digital out +/** + * snd_hda_multi_out_dig_close - release the digital out stream */ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3510,8 +3832,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); -/* - * set up more restrictions for analog out +/** + * snd_hda_multi_out_analog_open - open analog outputs + * + * Open analog outputs and set up the hw-constraints. + * If the digital outputs can be opened as slave, open the digital + * outputs, too. */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3556,9 +3882,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); -/* - * set up the i/o for analog out - * when the digital out is available, copy the front out to digital out, too. +/** + * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. + * + * Set up the i/o for analog out. + * When the digital out is available, copy the front out to digital out, too. */ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3615,8 +3943,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); -/* - * clean up the setting for analog out +/** + * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out */ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -4002,8 +4330,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume); * generic arrays */ -/* get a new element from the given array - * if it exceeds the pre-allocated array size, re-allocate the array +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. */ void *snd_array_new(struct snd_array *array) { @@ -4027,7 +4361,10 @@ void *snd_array_new(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_new); -/* free the given array elements */ +/** + * snd_array_free - free the given array elements + * @array: the array object + */ void snd_array_free(struct snd_array *array) { kfree(array->list); @@ -4037,7 +4374,12 @@ void snd_array_free(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_free); -/* +/** + * snd_print_pcm_rates - Print the supported PCM rates to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * * used by hda_proc.c and hda_eld.c */ void snd_print_pcm_rates(int pcm, char *buf, int buflen) @@ -4056,6 +4398,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen) } EXPORT_SYMBOL_HDA(snd_print_pcm_rates); +/** + * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * + * used by hda_proc.c and hda_eld.c + */ void snd_print_pcm_bits(int pcm, char *buf, int buflen) { static unsigned int bits[] = { 8, 16, 20, 24, 32 }; -- cgit v0.10.2 From 9bb1fe390de3e1def0dd162dbdaf62e0981105fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 15:33:49 +0100 Subject: ALSA: hda - Fix beep_mode option value The beep_mode option value was wrongly defined: it must be 0 = off and 1 = on. Also, evaluate the beep_mode value at snd_hda_attach_beep_device() properly so that no device is created when beep_mode=0 is given. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 9e48798..5fe34a8 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -215,7 +215,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) struct hda_beep *beep; if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly */ + return 0; /* disabled explicitly by hints */ + if (codec->beep_mode == HDA_BEEP_MODE_OFF) + return 0; /* disabled by module option */ beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (beep == NULL) diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 17dd1c3..f1de1ba 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -24,8 +24,8 @@ #include "hda_codec.h" -#define HDA_BEEP_MODE_ON 0 -#define HDA_BEEP_MODE_OFF 1 +#define HDA_BEEP_MODE_OFF 0 +#define HDA_BEEP_MODE_ON 1 #define HDA_BEEP_MODE_SWREG 2 /* beep information */ -- cgit v0.10.2 From 67d634c07afd8f70973d925463e775fdb89ad536 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 15:35:59 +0100 Subject: ALSA: hda - Fix build errors with CONFIG_SND_HDA_INPUT_BEEP=n Disable beep-related codes when CONFIG_SND_HDA_INPUT_BEEP isn't set. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e344235..2be61b3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1959,6 +1959,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /** * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch * @@ -1975,6 +1976,7 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ /* * bound volume controls diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7c04983..d4a3d09 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -77,6 +77,7 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ @@ -85,6 +86,11 @@ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#else +/* no digital beep - just the standard one */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ /* special beep mono mute switch */ #define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) @@ -108,8 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#ifdef CONFIG_SND_HDA_INPUT_BEEP int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#endif /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index ceb0c60..8a1064b 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -156,6 +156,7 @@ static const char *ad_slave_sws[] = { static void ad198x_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), @@ -165,6 +166,9 @@ static struct snd_kcontrol_new ad_beep_mixer[] = { #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif static int ad198x_build_controls(struct hda_codec *codec) { @@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->beep_amp) { struct snd_kcontrol_new *knew; for (knew = ad_beep_mixer; knew->name; knew++) { @@ -209,6 +214,7 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } } +#endif /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eee3143..ef7d210 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2410,12 +2410,14 @@ static const char *alc_slave_sws[] = { static void alc_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; +#endif static int alc_build_controls(struct hda_codec *codec) { @@ -2452,6 +2454,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* create beep controls if needed */ if (spec->beep_amp) { struct snd_kcontrol_new *knew; @@ -2467,6 +2470,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } } +#endif /* if we have no master control, let's create it */ if (!spec->no_analog && @@ -4780,8 +4784,12 @@ static void set_capture_mixer(struct hda_codec *codec) } } +#ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif /* * OK, here we have finally the patch for ALC880 -- cgit v0.10.2 From c5b5165ce28099484d5fa733abeae48540680440 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 Nov 2009 16:01:58 +0100 Subject: ALSA: hda - Disable default quirk for Sony VAIO with ALC262 codec The ALC262 has a quirk entry matching with all Sony Vaio laptops to use model=sony-assamd as default. But, model=auto works much better for new models in the recent driver versions, thus it's safer to disable that default quirk entry. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ba339d7..5784205 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11471,8 +11471,10 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), +#if 0 /* disable the quirk since model=auto works better in recent versions */ SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", ALC262_SONY_ASSAMD), +#endif SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", ALC262_TOSHIBA_RX1), SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), -- cgit v0.10.2 From 6f539a98614a014a7d6b64ab62b0dddb14e2d8cc Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:37:59 +0800 Subject: ALSA: intelhdmi - fix audio infoframe fill size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 4f25f08..ad1aa5d 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -509,12 +509,12 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec, hdmi_debug_dip_size(codec, pin_nid); hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - for (i = 0; i < sizeof(ai); i++) + for (i = 0; i < sizeof(*ai); i++) sum += params[i]; ai->checksum = - sum; hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(ai); i++) + for (i = 0; i < sizeof(*ai); i++) hdmi_write_dip_byte(codec, pin_nid, params[i]); } -- cgit v0.10.2 From 1e7c10fefadb42d9300305c7de57bea365855e9b Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:00 +0800 Subject: ALSA: intelhdmi - fix channel mapping slot mask Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index ad1aa5d..82312c6 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -433,7 +433,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0x7); + slot >> 4, slot & 0xf); } #endif } -- cgit v0.10.2 From 23ccc2bd246a5bdb1ac03dc9040a0585c1890ef3 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:01 +0800 Subject: ALSA: intelhdmi - export monitor-presence and ELD-valid status Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 20fa6ae..de50cfc 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -477,6 +477,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; + snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present); + snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); @@ -518,7 +520,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, * monitor_name manufacture_id product_id * eld_version edid_version */ - if (!strcmp(name, "connection_type")) + if (!strcmp(name, "monitor_present")) + e->monitor_present = val; + else if (!strcmp(name, "eld_valid")) + e->eld_valid = val; + else if (!strcmp(name, "connection_type")) e->conn_type = val; else if (!strcmp(name, "port_id")) e->port_id = val; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index d4a3d09..070b743 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -569,9 +569,11 @@ struct cea_sad { * ELD: EDID Like Data */ struct hdmi_eld { + bool monitor_present; + bool eld_valid; int eld_size; int baseline_len; - int eld_ver; /* (eld_ver == 0) indicates invalid ELD */ + int eld_ver; int cea_edid_ver; char monitor_name[ELD_MAX_MNL + 1]; int manufacture_id; diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 82312c6..095c993 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -62,8 +62,6 @@ struct intel_hdmi_spec { /* * HDMI sink attached to each pin */ - bool sink_present[INTEL_HDMI_PINS]; - bool sink_eldv[INTEL_HDMI_PINS]; struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; /* @@ -645,7 +643,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < spec->num_pins; i++) { if (spec->pin_cvt[i] != nid) continue; - if (spec->sink_present[i] != true) + if (!spec->sink_eld[i].monitor_present) continue; pin_nid = spec->pin[i]; @@ -675,8 +673,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) if (index < 0) return; - spec->sink_present[index] = pind; - spec->sink_eldv[index] = eldv; + spec->sink_eld[index].monitor_present = pind; + spec->sink_eld[index].eld_valid = eldv; if (pind && eldv) { hdmi_parse_eld(codec, index); -- cgit v0.10.2 From 864f92be7e8d4a0ba11d912e3f03d1a92a031dee Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:02 +0800 Subject: ALSA: hda - introduce snd_hda_jack_detect() and snd_hda_pin_sense() This helps merge duplicate code. v2: add snd_hda_jack_detect() and comments recommended by Takashi. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2be61b3..9cfdb77 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1317,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); +/** + * snd_hda_pin_sense - execute pin sense measurement + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Execute necessary pin sense measurement and return its Presence Detect, + * Impedance, ELD Valid etc. status bits. + */ +u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + u32 pincap = snd_hda_query_pin_caps(codec, nid); + + if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + + return snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); +} +EXPORT_SYMBOL_HDA(snd_hda_pin_sense); + +/** + * snd_hda_jack_detect - query pin Presence Detect status + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Query and return the pin's Presence Detect status. + */ +int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + u32 sense = snd_hda_pin_sense(codec, nid); + return !!(sense & AC_PINSENSE_PRESENCE); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_detect); + /* * read the current volume to info * if the cache exists, read the cache value. diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index de50cfc..4228f2f 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -309,17 +309,12 @@ out_fail: return -EINVAL; } -static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); -} - static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) { int eldv; int present; - present = hdmi_present_sense(codec, nid); + present = snd_hda_pin_sense(codec, nid); eldv = (present & AC_PINSENSE_ELDV); present = (present & AC_PINSENSE_PRESENCE); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 070b743..5778ae8 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -461,6 +461,8 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); +u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); struct hda_nid_item { struct snd_kcontrol *kctl; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 9ac09e4..2439e84 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int caps, present, hp_present; + unsigned int caps, hp_present; hda_nid_t nid; int i; @@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec) caps = snd_hda_query_pin_caps(codec, nid); if (!(caps & AC_PINCAP_PRES_DETECT)) continue; - if (caps & AC_PINCAP_TRIG_REQ) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - hp_present |= (present & AC_PINSENSE_PRESENCE) != 0; + hp_present = snd_hda_jack_detect(codec, nid); if (hp_present) break; } @@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec) struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; - unsigned int caps, present; + unsigned int present; nid = cfg->input_pins[spec->automic_idx]; - caps = snd_hda_query_pin_caps(codec, nid); - if (caps & AC_PINCAP_TRIG_REQ) - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (present & AC_PINSENSE_PRESENCE) + present = snd_hda_jack_detect(codec, nid); + if (present) change_cur_input(codec, spec->automic_idx, 0); else { unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5784205..cbb2d32 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -961,18 +961,12 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, static void alc_automute_pin(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present, pincap; unsigned int nid = spec->autocfg.hp_pins[0]; int i; if (!nid) return; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, nid); for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { nid = spec->autocfg.speaker_pins[i]; if (!nid) @@ -1012,9 +1006,7 @@ static void alc_mic_automute(struct hda_codec *codec) cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; - present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0, - AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, spec->ext_mic.pin); if (present) { alive = &spec->ext_mic; dead = &spec->int_mic; @@ -1513,7 +1505,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { static void alc_automute_amp(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int val, mute, pincap; + unsigned int mute; hda_nid_t nid; int i; @@ -1522,13 +1514,7 @@ static void alc_automute_amp(struct hda_codec *codec) nid = spec->autocfg.hp_pins[i]; if (!nid) break; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (val & AC_PINSENSE_PRESENCE) { + if (snd_hda_jack_detect(codec, nid)) { spec->jack_present = 1; break; } @@ -2784,8 +2770,7 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x18); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } @@ -5102,11 +5087,8 @@ static struct hda_verb alc260_hp_unsol_verbs[] = { static void alc260_hp_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x10, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x10); alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); } @@ -5171,11 +5153,8 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = { static void alc260_hp_3013_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x15); alc260_hp_master_update(codec, 0x15, 0x10, 0x11); } @@ -5188,12 +5167,8 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec, static void alc260_hp_3012_automute(struct hda_codec *codec) { - unsigned int present, bits; + unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT; - present = snd_hda_codec_read(codec, 0x10, 0, - AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; - - bits = present ? 0 : PIN_OUT; snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, bits); snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, @@ -5763,8 +5738,7 @@ static void alc260_replacer_672v_automute(struct hda_codec *codec) unsigned int present; /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ - present = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x0f); if (present) { snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 1); @@ -8196,12 +8170,8 @@ static void alc883_mitac_setup(struct hda_codec *codec) /* static void alc883_mitac_mic_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } */ @@ -8423,10 +8393,8 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = { /* toggle front-jack and RCA according to the hp-jack state */ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x1b); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, @@ -8436,10 +8404,8 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) /* toggle RCA according to the front-jack state */ static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x14); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -8532,24 +8498,16 @@ static void alc883_haier_w66_setup(struct hda_codec *codec) static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0; - present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); } static void alc883_lenovo_101e_all_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -8700,8 +8658,7 @@ static void alc889A_mb31_automute(struct hda_codec *codec) /* Mute only in 2ch or 4ch mode */ if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) == 0x00) { - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x15); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, @@ -10044,10 +10001,8 @@ static void alc262_hp_master_update(struct hda_codec *codec) static void alc262_hp_bpc_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int presence; - presence = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + + spec->jack_present = snd_hda_jack_detect(codec, 0x1b); alc262_hp_master_update(codec); } @@ -10061,10 +10016,8 @@ static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res) static void alc262_hp_wildwest_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int presence; - presence = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + + spec->jack_present = snd_hda_jack_detect(codec, 0x15); alc262_hp_master_update(codec); } @@ -10298,13 +10251,8 @@ static void alc262_hippo_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, hp_nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + spec->jack_present = snd_hda_jack_detect(codec, hp_nid); alc262_hippo_master_update(codec); } @@ -10630,21 +10578,8 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - /* check laptop HP jack */ - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - /* check docking HP jack */ - present |= snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (present & AC_PINSENSE_PRESENCE) - spec->jack_present = 1; - else - spec->jack_present = 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x14) || + snd_hda_jack_detect(codec, 0x1b); spec->sense_updated = 1; } /* unmute internal speaker only if both HPs are unplugged and @@ -10689,12 +10624,7 @@ static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present_int_hp; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - present_int_hp = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x1b); spec->sense_updated = 1; } if (spec->jack_present) { @@ -10886,12 +10816,7 @@ static void alc262_ultra_automute(struct hda_codec *codec) mute = 0; /* auto-mute only when HP is used as HP */ if (!spec->cur_mux[0]) { - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x15); if (spec->jack_present) mute = HDA_AMP_MUTE; } @@ -11933,10 +11858,7 @@ static void alc268_acer_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x14); spec->sense_updated = 1; } if (spec->jack_present) @@ -12055,8 +11977,7 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -13039,8 +12960,7 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -13065,12 +12985,10 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec) unsigned char bits; /* Check laptop headphone socket */ - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); /* Check port replicator headphone socket */ - present |= snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present |= snd_hda_jack_detect(codec, 0x1a); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, @@ -13094,11 +13012,8 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) unsigned int present_laptop; unsigned int present_dock; - present_laptop = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - - present_dock = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present_laptop = snd_hda_jack_detect(codec, 0x18); + present_dock = snd_hda_jack_detect(codec, 0x1b); /* Laptop mic port overrides dock mic port, design decision */ if (present_dock) @@ -13183,8 +13098,7 @@ static void alc269_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -14162,10 +14076,8 @@ static struct hda_verb alc861_toshiba_init_verbs[] = { /* toggle speaker-output according to the hp-jack state */ static void alc861_toshiba_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x0f); - present = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, @@ -15070,9 +14982,9 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x18); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } @@ -16383,9 +16295,9 @@ static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x14); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); } @@ -16395,9 +16307,9 @@ static void alc662_lenovo_101e_all_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1b); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -16456,9 +16368,7 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16471,9 +16381,7 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16490,9 +16398,7 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16509,9 +16415,7 @@ static void alc662_f5z_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x1b); bits = present ? 0 : PIN_OUT; snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, bits); @@ -16521,12 +16425,8 @@ static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec) { unsigned int present1, present2; - present1 = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - present2 = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present1 = snd_hda_jack_detect(codec, 0x21); + present2 = snd_hda_jack_detect(codec, 0x15); if (present1 || present2) { snd_hda_codec_write_cache(codec, 0x14, 0, @@ -16541,12 +16441,8 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) { unsigned int present1, present2; - present1 = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - present2 = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present1 = snd_hda_jack_detect(codec, 0x1b); + present2 = snd_hda_jack_detect(codec, 0x15); if (present1 || present2) { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, @@ -16706,9 +16602,7 @@ static void alc663_g71v_hp_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); @@ -16721,9 +16615,7 @@ static void alc663_g71v_front_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); -- cgit v0.10.2 From 3f54aa5091f48e9d8ce6e99b248449d08acccb26 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:03 +0800 Subject: ALSA: intelhdmi - probe for monitor/eld presence at module init time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids lost of presence info on module reloading. The presence info used to be only updated at the (rare) hotplug events. Proposed by David, thanks! CC: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 095c993..c5fd011 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -259,6 +259,25 @@ static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) return 0; } +static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + snd_hdmi_show_eld(eld); +} + +static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + int present = snd_hda_pin_sense(codec, pin_nid); + + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + + if (present & AC_PINSENSE_ELDV) + hdmi_get_show_eld(codec, pin_nid, eld); +} + static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct intel_hdmi_spec *spec = codec->spec; @@ -269,6 +288,8 @@ static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } + hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + spec->pin[spec->num_pins] = pin_nid; spec->num_pins++; @@ -436,15 +457,6 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) #endif } -static void hdmi_parse_eld(struct hda_codec *codec, int index) -{ - struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld[index]; - - if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) - snd_hdmi_show_eld(eld); -} - /* * Audio InfoFrame routines @@ -677,7 +689,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) spec->sink_eld[index].eld_valid = eldv; if (pind && eldv) { - hdmi_parse_eld(codec, index); + hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]); /* TODO: do real things about ELD */ } } -- cgit v0.10.2 From 978be6d711be237e0344eca21c3922ae88a240bc Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:04 +0800 Subject: ALSA: intelhdmi - separate out infoframe checksum routine And make it right when called for more than one times. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index c5fd011..d68dba9 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -508,24 +508,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) #endif } +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 sum = 0; + int i; + + ai->checksum = 0; + + for (i = 0; i < sizeof(*ai); i++) + sum += bytes[i]; + + ai->checksum = - sum; +} + static void hdmi_fill_audio_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, struct hdmi_audio_infoframe *ai) { - u8 *params = (u8 *)ai; - u8 sum = 0; + u8 *bytes = (u8 *)ai; int i; hdmi_debug_dip_size(codec, pin_nid); hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - for (i = 0; i < sizeof(*ai); i++) - sum += params[i]; - ai->checksum = - sum; + hdmi_checksum_audio_infoframe(ai); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < sizeof(*ai); i++) - hdmi_write_dip_byte(codec, pin_nid, params[i]); + hdmi_write_dip_byte(codec, pin_nid, bytes[i]); } /* -- cgit v0.10.2 From 848de598eef9603d6f2c174f90fded4e63ac5e23 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:05 +0800 Subject: ALSA: intelhdmi - sticky infoframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remember the active infoframe, so as to avoid stop/restart infoframe transmission when switching between audio clips of the same format. Proposed by Shang and David. CC: Shane W CC: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index d68dba9..abb056f 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -646,6 +646,27 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, hdmi_debug_channel_mapping(codec, nid); } +static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 val; + int i; + + if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) + != AC_DIPXMIT_BEST) + return false; + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) { + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_DATA, 0); + if (val != bytes[i]) + return false; + } + + return true; +} static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) @@ -670,8 +691,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, continue; pin_nid = spec->pin[i]; - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); + if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } } } @@ -767,16 +791,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != hinfo->nid) - continue; - - hdmi_stop_infoframe_trans(codec, spec->pin[i]); - } - snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v0.10.2 From 5779191e0efd851fb0d54698c13cb4f5325caca6 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:06 +0800 Subject: ALSA: intelhdmi - sticky stream id and format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We tracked down the first-0.5s-hdmi-audio-samples-lost problem to the AC_VERB_SET_CHANNEL_STREAMID command. It is suspected that many HDMI sinks need some time to adapt to the new state. The workaround is to avoid changing stream id/format whenever possible. Proposed by David. Signed-off-by: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index abb056f..8a1cf9d 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -772,6 +772,31 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ +static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, int format) +{ + int tag; + int fmt; + + tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; + fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); + + snd_printdd("hdmi_setup_stream: " + "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", + nid, + tag == stream_tag ? "" : "new-", + stream_tag, + fmt == format ? "" : "new-", + format); + + if (tag != stream_tag) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4); + if (fmt != format) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); +} + static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -783,7 +808,7 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); return 0; } @@ -791,7 +816,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v0.10.2 From 81bf31e2d0a6a9f5d83da0a757f8ca03db908162 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:07 +0800 Subject: ALSA: intelhdmi - sticky channel count Don't change channel count if not necessary. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 8a1cf9d..928df59 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -422,24 +422,18 @@ static void hdmi_stop_infoframe_trans(struct hda_codec *codec, AC_DIPXMIT_DISABLE); } -#ifdef CONFIG_SND_DEBUG_VERBOSE static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } -#endif static void hdmi_set_channel_count(struct hda_codec *codec, hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); - -#ifdef CONFIG_SND_DEBUG_VERBOSE if (chs != hdmi_get_channel_count(codec, nid)) - snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec, nid)); -#endif + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) -- cgit v0.10.2 From 83d605fd63e704419ccb92d48b735c6890ce3d6a Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:08 +0800 Subject: ALSA: hda - show EPSS capability in proc Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index be6c5f4..2d62761 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -286,6 +286,10 @@ enum { #define AC_PWRST_D1SUP (1<<1) #define AC_PWRST_D2SUP (1<<2) #define AC_PWRST_D3SUP (1<<3) +#define AC_PWRST_D3COLDSUP (1<<4) +#define AC_PWRST_S3D3COLDSUP (1<<29) +#define AC_PWRST_CLKSTOP (1<<30) +#define AC_PWRST_EPSS (1U<<31) /* Power state values */ #define AC_PWRST_SETTING (0xf<<0) diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f465cff..09476fc 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -26,6 +26,21 @@ #include "hda_codec.h" #include "hda_local.h" +static char *bits_names(unsigned int bits, char *names[], int size) +{ + int i, n; + static char buf[128]; + + for (i = 0, n = 0; i < size; i++) { + if (bits & (1U<> -- cgit v0.10.2 From d56757abc11a21996d9839c0d4e3b2c3666cd318 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 08:00:14 +0100 Subject: ALSA: hda - Replace the rest of jack-detections with snd_hda_jack_detect() Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8a1064b..455a049 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -720,10 +720,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { static void ad1986a_automic(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0); + present = snd_hda_jack_detect(codec, 0x1f); /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - (present & AC_PINSENSE_PRESENCE) ? 0 : 2); + present ? 0 : 2); } #define AD1986A_MIC_EVENT 0x36 @@ -762,10 +762,8 @@ static void ad1986a_update_hp(struct hda_codec *codec) static void ad1986a_hp_automute(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(present & 0x80000000); + spec->jack_present = snd_hda_jack_detect(codec, 0x1a); if (spec->inv_jack_detect) spec->jack_present = !spec->jack_present; ad1986a_update_hp(codec); @@ -1555,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x06, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x06); snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -1576,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x08, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x08); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -2532,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) { if ((res >> 26) != AD1988_HP_EVENT) return; - if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31)) + if (snd_hda_jack_detect(codec, 0x11)) snd_hda_sequence_write(codec, ad1988_laptop_hp_on); else snd_hda_sequence_write(codec, ad1988_laptop_hp_off); @@ -3778,8 +3774,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x11); snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, @@ -3791,8 +3786,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x14); snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, present ? 0 : 1); } @@ -3827,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; - if (!present) { - present = snd_hda_codec_read(codec, 0x12, 0, - AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; - } + present = snd_hda_jack_detect(codec, 0x11); + if (!present) + present = snd_hda_jack_detect(codec, 0x12); snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, @@ -3845,11 +3835,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec) { unsigned int idx; - if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE) + if (snd_hda_jack_detect(codec, 0x14)) idx = 0; - else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE) + else if (snd_hda_jack_detect(codec, 0x1c)) idx = 4; else idx = 1; @@ -4018,8 +4006,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x11); snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -4127,14 +4114,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* switch to external mic if plugged */ static void ad1984a_touchsmart_automic(struct hda_codec *codec) { - if (snd_hda_codec_read(codec, 0x1c, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) { + if (snd_hda_jack_detect(codec, 0x1c)) snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, 0x4); - } else { + else snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, 0x5); - } } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 905859d..0b097fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -397,9 +397,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) for (i = 0; i < spec->jacks.used; i++) { if (jacks->nid == nid) { unsigned int present; - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, nid); present = (present) ? jacks->type : 0 ; @@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x12, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x12); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int bits; - spec->hp_present = snd_hda_codec_read(codec, 0x11, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + spec->hp_present = snd_hda_jack_detect(codec, 0x11); bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, @@ -1243,8 +1239,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int bits; - spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + spec->hp_present = snd_hda_jack_detect(codec, 0x13); bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; /* See the note in cxt5047_hp_master_sw_put */ @@ -1267,8 +1262,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -1621,9 +1615,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec) if (spec->no_auto_mic) return; - present = snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x17); snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_CONNECT_SEL, present ? 0x01 : 0x00); @@ -1638,9 +1630,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) if (spec->no_auto_mic) return; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x18); if (present) spec->cur_adc_idx = 1; else @@ -1661,9 +1651,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + spec->hp_present = snd_hda_jack_detect(codec, 0x16); cxt5051_update_speaker(codec); } @@ -2011,8 +1999,7 @@ static void cxt5066_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1a); if (present) { snd_printdd("CXT5066: external microphone detected\n"); snd_hda_sequence_write(codec, ext_mic_present); @@ -2029,12 +2016,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec) unsigned int portA, portD; /* Port A */ - portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + portA = snd_hda_jack_detect(codec, 0x19); /* Port D */ - portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE) << 1; + portD = snd_hda_jack_detect(codec, 0x1c); spec->hp_present = !!(portA | portD); snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cbb2d32..28acbe6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8446,8 +8446,7 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x18); snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7f76a97..d83649c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4413,14 +4413,11 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; - if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) - & (1 << 31)) - return 1; - return 0; + return snd_hda_jack_detect(codec, nid); } static void stac92xx_line_out_detect(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0c621d7..b70e26a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -547,8 +547,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned no_presence = (def_conf & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - unsigned present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0) >> 31; + unsigned present = snd_hda_jack_detect(codec, nid); struct via_spec *spec = codec->spec; if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) || ((no_presence || present) @@ -786,14 +785,11 @@ static void set_jack_power_state(struct hda_codec *codec) /* Mono out */ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_codec_read( - codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1c); if (present) mono_out = 0; else { - present = snd_hda_codec_read( - codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) - & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1d); if (!spec->hp_independent_mode && present) mono_out = 0; else @@ -872,8 +868,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Class-D */ /* PW0 (24h), MW0(18h), MUX0(34h) */ - present = snd_hda_codec_read( - codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x25); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { @@ -894,8 +889,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Mono Out */ /* PW15 (31h), MW8(17h), MUX8(3bh) */ - present = snd_hda_codec_read( - codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x26); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { @@ -973,8 +967,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Internal Speaker */ /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_codec_read( - codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x25); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { @@ -994,8 +987,7 @@ static void set_jack_power_state(struct hda_codec *codec) } /* Mono Out */ /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_codec_read( - codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x28); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { @@ -1920,8 +1912,7 @@ static void via_hp_automute(struct hda_codec *codec) unsigned int present = 0; struct via_spec *spec = codec->spec; - present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { struct snd_ctl_elem_id id; @@ -1947,9 +1938,8 @@ static void via_mono_automute(struct hda_codec *codec) if (spec->codec_type != VT1716S) return; - lineout_present = snd_hda_codec_read( - codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + lineout_present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); /* Mute Mono Out if Line Out is plugged */ if (lineout_present) { @@ -1958,9 +1948,7 @@ static void via_mono_automute(struct hda_codec *codec) return; } - hp_present = snd_hda_codec_read( - codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) snd_hda_codec_amp_stereo( @@ -2025,8 +2013,7 @@ static void via_speaker_automute(struct hda_codec *codec) if (spec->codec_type != VT2002P && spec->codec_type != VT1812) return; - hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { struct snd_ctl_elem_id id; @@ -2055,11 +2042,9 @@ static void via_hp_bind_automute(struct hda_codec *codec) if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) return; - hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); if (!spec->hp_independent_mode) { /* Mute Line-Outs */ @@ -2529,8 +2514,7 @@ static void vt1708_update_hp_jack_state(struct work_struct *work) return; /* if jack state toggled */ if (spec->vt1708_hp_present - != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) >> 31)) { + != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { spec->vt1708_hp_present ^= 1; via_hp_automute(spec->codec); } -- cgit v0.10.2 From 8af3aeb498197f6fdf5acc913ffe8a392cb921c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 14:23:37 +0100 Subject: ALSA: hda - Fix detection of dual headphones The dual-headphone mode with STAC/IDT codecs is useful only for machines that have two (or more) built-in headphones. But, some HP laptops give multiple headphone pin configs, one for the built-in and another for the separate (likely a docking station) one. This results in a missing speaker volume control. This patch adds more check for the dual-headphone mode to avoid this problem. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d83649c..39001c4 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3635,6 +3635,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec) } } +static int is_dual_headphones(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i, valid_hps; + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT || + spec->autocfg.hp_outs <= 1) + return 0; + valid_hps = 0; + for (i = 0; i < spec->autocfg.hp_outs; i++) { + hda_nid_t nid = spec->autocfg.hp_pins[i]; + unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE) + continue; + valid_hps++; + } + return (valid_hps > 1); +} + + static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in) { struct sigmatel_spec *spec = codec->spec; @@ -3651,8 +3671,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out /* If we have no real line-out pin and multiple hp-outs, HPs should * be set up as multi-channel outputs. */ - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.hp_outs > 1) { + if (is_dual_headphones(codec)) { /* Copy hp_outs to line_outs, backup line_outs in * speaker_outs so that the following routines can handle * HP pins as primary outputs. -- cgit v0.10.2 From b4e818768d50a5b7aa1635676839682bcf0691b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 17:20:24 +0100 Subject: ALSA: hda - Fix mute-LED sync on HP laptops with IDT92HD83xxx codecs The mute-LED isn't synchronized with the actual mute state on some HP laptops with IDT 92HD83xxx codecs. A similar hack using check_power_status callback is added for this codec, too. Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 4c7f9ae..9000cd8 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -391,6 +391,7 @@ STAC92HD83* ref Reference board mic-ref Reference board with power management for ports dell-s14 Dell laptop + hp HP laptops with (inverted) mute-LED auto BIOS setup (default) STAC9872 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 39001c4..2a45375 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -93,6 +93,7 @@ enum { STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, + STAC_92HD83XXX_HP, STAC_92HD83XXX_MODELS }; @@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", + [STAC_92HD83XXX_HP] = "hp", }; static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1634,6 +1636,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD83XXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, + "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; @@ -4834,6 +4838,23 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, return 0; } + +static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec, + hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + + if (nid != 0x13) + return 0; + if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) + spec->gpio_data |= spec->gpio_led; /* mute LED on */ + else + spec->gpio_data &= ~spec->gpio_led; /* mute LED off */ + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + + return 0; +} + #endif static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) @@ -5199,6 +5220,22 @@ again: break; } + codec->patch_ops = stac92xx_patch_ops; + + if (spec->board_config == STAC_92HD83XXX_HP) + spec->gpio_led = 0x01; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (spec->gpio_led) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + /* register check_power_status callback. */ + codec->patch_ops.check_power_status = + idt92hd83xxx_hp_check_power_status; + } +#endif + err = stac92xx_parse_auto_config(codec, 0x1d, 0); if (!err) { if (spec->board_config < 0) { @@ -5234,8 +5271,6 @@ again: snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, num_dacs); - codec->patch_ops = stac92xx_patch_ops; - codec->proc_widget_hook = stac92hd_proc_hook; return 0; -- cgit v0.10.2 From f2624791a0c2a2d7664b12d75ca327917141fd3b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Nov 2009 11:48:44 +0100 Subject: ALSA: hda - Change quirk for Acer Aspire 5930G Change the quirk for Acer Aspire 5930G from model=acer-aspire-4930g to model=acer-aspre-6530g. The tuba bass gets muted along with the other built-in speakers upon headphones insertion, the internal mic works perfectly etc. Reported-by: Claudio Viano Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 28acbe6..d29fa18 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8754,7 +8754,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_4930G), + ALC888_ACER_ASPIRE_6530G), SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", -- cgit v0.10.2 From 7cef4cf1c5e9d81554137f52b96a5ab7f6241cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wojni=C5=82owicz?= Date: Fri, 20 Nov 2009 12:14:35 +0100 Subject: ALSA: hda - 4930g mute lfe and side when pluging in headphones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes first issue from comment 0021423 in bug 0004317 for Acer Aspire 5930g Signed-off-by: Łukasz Wojniłowicz Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d29fa18..eedbe19 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1772,6 +1772,8 @@ static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; } static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) -- cgit v0.10.2 From fc08722510494e8185e176713de8c47238512591 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 21 Nov 2009 19:57:11 +0100 Subject: ALSA: hda - Fix input and jack Kconfig depenencies CONFIG_SND_JACK needs to be selected explicitly only when INPUT=y or INPUT_SND. The current way, INPUT=SND_HDA_INTEL isn't strict enough. Reported-by: Randy Dunlap Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 25ae10e..556cff9 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -51,7 +51,7 @@ config SND_HDA_INPUT_BEEP_MODE config SND_HDA_INPUT_JACK bool "Support jack plugging notification via input layer" - depends on INPUT=y || INPUT=SND_HDA_INTEL + depends on INPUT=y || INPUT=SND select SND_JACK help Say Y here to enable the jack plugging notification via -- cgit v0.10.2 From 83dd7408b59c1945069199d712df8c7c64a76e1f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Nov 2009 08:57:53 +0100 Subject: Revert "ALSA: hda - Change quirk for Acer Aspire 5930G" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f2624791a0c2a2d7664b12d75ca327917141fd3b. Łukasz Wojniłowicz reported that the change causes both internal and external mics not working any more. The headphone jacking issue was fixed by his previous patch, it's better to revert to acer-aspire-4930g model. Reported-by: Łukasz Wojniłowicz Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eedbe19..7e8b17a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8756,7 +8756,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_6530G), + ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", -- cgit v0.10.2 From 95a618bdac29c7b0f1a516aec9fc37626dec1af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20R=C3=BCnkaru?= Date: Mon, 23 Nov 2009 22:23:49 +0200 Subject: ALSA: hda - Make Dell Vostro 1015n mic and speaker switching work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dell Vostro 1015n uses Conexant CX20583-10Z (0x14f1:5067). Patch is based on "olpc-xo-1_5" branch. Dell uses digital mic. Signed-off-by: Einar Rünkaru Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 0b097fa..36dd5a6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2009,6 +2009,46 @@ static void cxt5066_automic(struct hda_codec *codec) } } +/* toggle input of built-in digital mic and mic jack appropriately */ +static void cxt5066_vostro_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int present; + + struct hda_verb ext_mic_present[] = { + /* enable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, + + /* switch to external mic input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0}, + + /* disable internal digital mic */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb ext_mic_absent[] = { + /* enable internal mic, port C */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* switch to internal mic input */ + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, + + /* disable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + + present = snd_hda_jack_detect(codec, 0x1a); + if (present) { + snd_printdd("CXT5066: external microphone detected\n"); + snd_hda_sequence_write(codec, ext_mic_present); + } else { + snd_printdd("CXT5066: external microphone absent\n"); + snd_hda_sequence_write(codec, ext_mic_absent); + } +} + /* mute internal speaker if HP is plugged */ static void cxt5066_hp_automute(struct hda_codec *codec) { @@ -2041,6 +2081,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) } } +/* unsolicited event for jack sensing */ +static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_vostro_automic(codec); + break; + } +} + static const struct hda_input_mux cxt5066_analog_mic_boost = { .num_items = 5, .items = { @@ -2282,6 +2336,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { { } /* end */ }; +static struct hda_verb cxt5066_init_verbs_vostro[] = { + /* Port A: headphones */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* Port B: external microphone */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port C: unused */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port D: unused */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port E: unused, but has primary EAPD */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + /* Port F: unused */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port G: internal speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* DAC2: unused */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* Digital microphone port */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* Audio input selectors */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + + /* Disable SPDIF */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* enable unsolicited events for Port A and B */ + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + static struct hda_verb cxt5066_init_verbs_portd_lo[] = { {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, { } /* end */ @@ -2303,6 +2418,7 @@ enum { CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ + CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ CXT5066_MODELS }; @@ -2310,6 +2426,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = { [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", + [CXT5066_DELL_VOSTO] = "dell-vostro" }; static struct snd_pci_quirk cxt5066_cfg_tbl[] = { @@ -2318,6 +2435,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), {} }; @@ -2385,6 +2503,19 @@ static int patch_cxt5066(struct hda_codec *codec) /* input source automatically selected */ spec->input_mux = NULL; break; + case CXT5066_DELL_VOSTO: + codec->patch_ops.unsol_event = cxt5066_vostro_event; + spec->init_verbs[0] = cxt5066_init_verbs_vostro; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->port_d_mode = 0; + + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + + /* input source automatically selected */ + spec->input_mux = NULL; + break; } return 0; @@ -2402,6 +2533,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5051 }, { .id = 0x14f15066, .name = "CX20582 (Pebble)", .patch = patch_cxt5066 }, + { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", + .patch = patch_cxt5066 }, {} /* terminator */ }; @@ -2409,6 +2542,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045"); MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); MODULE_ALIAS("snd-hda-codec-id:14f15066"); +MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -- cgit v0.10.2 From 0b587fc4d35afb1bc0fc3d890084bb14c78372dc Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 25 Nov 2009 18:27:20 -0500 Subject: ALSA: hda: Fix max PCM level to 0 dB for Fujitsu-Siemens laptops using CX20549 (Venice) BugLink: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4792 Cristian reported that these models have really bad sound above 6 dB and proposed the original patch. I've updated the comment to reflect this change. Signed-off-by: Daniel T Chen Reported-by: Cristian Klein Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 36dd5a6..60810ba 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1171,9 +1171,10 @@ static int patch_cxt5045(struct hda_codec *codec) switch (codec->subsystem_id >> 16) { case 0x103c: - /* HP laptop has a really bad sound over 0dB on NID 0x17. - * Fix max PCM level to 0 dB - * (originall it has 0x2b steps with 0dB offset 0x14) + case 0x1734: + /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB + * on NID 0x17. Fix max PCM level to 0 dB + * (originally it has 0x2b steps with 0dB offset 0x14) */ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, (0x14 << AC_AMPCAP_OFFSET_SHIFT) | -- cgit v0.10.2 From bfc9902599549736b9c6445e1e2235b8542f64a6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Nov 2009 12:22:44 +0100 Subject: ALSA: hda - Don't trigger pin-sense for STAC/IDT codecs STAC/IDT codecs seem to behave weird when SET_PIN_SENSE verb is issued before reading the jack-detection although the TRIG_REQ pin capability is given by the hardware. Since snd_hda_jack_detect() issues the SET_PIN_SENSE verb simply judging from the pincap, we have to revert the change in the commit d56757abc11a21996d9839c0d4e3b2c3666cd318 ALSA: hda - Replace the rest of jack-detections with snd_hda_jack_detect() to plain GET_PIN_SENSE verb without triggering. Reported-by: Jiri Slaby Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 2a45375..6b0bc04 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4440,7 +4440,14 @@ static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; - return snd_hda_jack_detect(codec, nid); + /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT + * codecs behave wrongly when SET_PIN_SENSE is triggered, although + * the pincap gives TRIG_REQ bit. + */ + if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE) + return 1; + return 0; } static void stac92xx_line_out_detect(struct hda_codec *codec, -- cgit v0.10.2 From 45d4ebf1a6255f2234a041685789cbecac3453f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Nov 2009 11:58:30 +0100 Subject: ALSA: hda - Add a position_fix quirk for MSI Wind U115 MSI Wind U115 seems to require position_fix=1 explicitly. Otherwise it screws up PulseAudio. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 91bcbda..238651b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2234,6 +2234,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), {} }; -- cgit v0.10.2 From 854206b074581957e7b5c955001c329f94986b4c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Nov 2009 18:22:04 +0100 Subject: ALSA: hda - Fix Cxt5047 test mode The NID 0x1a of Conexant 5047 chip is a mic boost volume only with the output amp unlike 5045 chip. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 60810ba..a09c03c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1410,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = { .get = conexant_mux_enum_get, .put = conexant_mux_enum_put, }, - HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT), { } /* end */ }; -- cgit v0.10.2 From cfc9b06f0befe50ef02253f72b76946363549031 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Dec 2009 12:19:37 +0100 Subject: ALSA: hda - Add a pin-fix for FSC Amilo Pi1505 FSC Amilo Pi 1505 has a buggy BIOS and doesn't set up the HP and speaker pins properly. Add the pinfix entry for that. Reference: Novell bnc#557403 https://bugzilla.novell.com/show_bug.cgi?id=557403 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7e8b17a..a38a81e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14543,6 +14543,27 @@ static struct alc_config_preset alc861_presets[] = { }, }; +/* Pin config fixes */ +enum { + PINFIX_FSC_AMILO_PI1505, +}; + +static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } +}; + +static const struct alc_fixup alc861_fixups[] = { + [PINFIX_FSC_AMILO_PI1505] = { + .pins = alc861_fsc_amilo_pi1505_pinfix + }, +}; + +static struct snd_pci_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), + {} +}; static int patch_alc861(struct hda_codec *codec) { @@ -14566,6 +14587,8 @@ static int patch_alc861(struct hda_codec *codec) board_config = ALC861_AUTO; } + alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups); + if (board_config == ALC861_AUTO) { /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); -- cgit v0.10.2 From 2f703e7a2ea5f6d5ea14a7b2cd0d31be07839ac6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Dec 2009 14:17:37 +0100 Subject: ALSA: hda - Add position_fix quirk for HP dv3 HP dv3 requires position_fix=1. Reference: Novell bnc#555935 https://bugzilla.novell.com/show_bug.cgi?id=555935 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 238651b..d822bfc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2233,6 +2233,7 @@ static int azx_dev_free(struct snd_device *device) static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), + SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), {} -- cgit v0.10.2 From 274693f37090ada2cadd09944ab883f05ea6ebe6 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 3 Dec 2009 10:07:50 +0100 Subject: ALSA: hda - Add ALC661/259, ALC892/888VD support Fixed List: 1. Add alc_read_coef_idx function 2. Add ALC661 ALC259 3. Add ALC892 ALC888VD Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a38a81e..98e117b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1394,6 +1394,17 @@ static void alc_pick_fixup(struct hda_codec *codec, add_verb(codec->spec, fix->verbs); } +static int alc_read_coef_idx(struct hda_codec *codec, + unsigned int coef_idx) +{ + unsigned int val; + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, + coef_idx); + val = snd_hda_codec_read(codec, 0x20, 0, + AC_VERB_GET_PROC_COEF, 0); + return val; +} + /* * ALC888 */ @@ -3472,7 +3483,7 @@ static int alc_build_pcms(struct hda_codec *codec) snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - + if (spec->stream_analog_playback) { if (snd_BUG_ON(!spec->multiout.dac_nids)) return -EINVAL; @@ -13445,6 +13456,13 @@ static int patch_alc269(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); + if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC259", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + } + board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, alc269_models, alc269_cfg_tbl); @@ -17444,6 +17462,13 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); + if (alc_read_coef_idx(codec, 0)==0x8020){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC661", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + } + board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, alc662_models, alc662_cfg_tbl); @@ -17510,6 +17535,20 @@ static int patch_alc662(struct hda_codec *codec) return 0; } +static int patch_alc888(struct hda_codec *codec) +{ + if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + patch_alc662(codec); + } else { + patch_alc882(codec); + } + return 0; +} + /* * patch entries */ @@ -17541,8 +17580,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 }, { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", .patch = patch_alc882 }, - { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 }, + { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 }, + { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 }, {} /* terminator */ }; -- cgit v0.10.2 From ac2c92e0cd06387ecee8115f5fa385fba6413c42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Dec 2009 10:14:10 +0100 Subject: ALSA: hda - Fix memory leaks in the previous patch The previous hack for replacing the codec name give memory leaks at error paths. This patch fixes them. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 98e117b..d967836 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13459,8 +13459,10 @@ static int patch_alc269(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC259", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; + } } board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, @@ -17465,8 +17467,10 @@ static int patch_alc662(struct hda_codec *codec) if (alc_read_coef_idx(codec, 0)==0x8020){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC661", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; + } } board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, @@ -17540,13 +17544,13 @@ static int patch_alc888(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; - patch_alc662(codec); - } else { - patch_alc882(codec); + } + return patch_alc662(codec); } - return 0; + return patch_alc882(codec); } /* -- cgit v0.10.2