From 9f4bd5dde81b5cb94e4f52f2f05825aa0422f1ff Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Sun, 1 Oct 2006 10:48:04 +0100 Subject: [ALSA] snd-emu10k1: Added support for emu1010, including E-Mu 1212m and E-Mu 1820m Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 315 ++++++++++++++++++++++- sound/pci/emu10k1/emu10k1_main.c | 540 ++++++++++++++++++++++++++++++--------- sound/pci/emu10k1/emu10k1x.c | 6 +- sound/pci/emu10k1/emufx.c | 102 +++++++- sound/pci/emu10k1/emumixer.c | 325 ++++++++++++++++++++++- sound/pci/emu10k1/emupcm.c | 127 ++++++--- sound/pci/emu10k1/emuproc.c | 34 ++- sound/pci/emu10k1/io.c | 45 ++++ sound/pci/emu10k1/p16v.c | 12 +- sound/pci/emu10k1/voice.c | 2 +- 10 files changed, 1328 insertions(+), 180 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 3d3c1514cf71..396812eb668d 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -188,7 +188,35 @@ #define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ /* NOTE: The rest of the bits in this register */ /* _are_ relevant under Linux. */ -#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_PUSH_BUTTON_ENABLE 0x00100000 /* Enables Volume Inc/Dec and Mute functions */ +#define HCFG_BAUD_RATE 0x00080000 /* 0 = 48kHz, 1 = 44.1kHz */ +#define HCFG_EXPANDED_MEM 0x00040000 /* 1 = any 16M of 4G addr, 0 = 32M of 2G addr */ +#define HCFG_CODECFORMAT_MASK 0x00030000 /* CODEC format */ + +/* Specific to Alice2, CA0102 */ +#define HCFG_CODECFORMAT_AC97_1 0x00000000 /* AC97 CODEC format -- Ver 1.03 */ +#define HCFG_CODECFORMAT_AC97_2 0x00010000 /* AC97 CODEC format -- Ver 2.1 */ +#define HCFG_AUTOMUTE_ASYNC 0x00008000 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_AUTOMUTE_SPDIF 0x00004000 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* the SPDIF V-bit indicates invalid audio */ +#define HCFG_EMU32_SLAVE 0x00002000 /* 0 = Master, 1 = Slave. Slave for EMU1010 */ +#define HCFG_SLOW_RAMP 0x00001000 /* Increases Send Smoothing time constant */ +/* 0x00000800 not used on Alice2 */ +#define HCFG_PHASE_TRACK_MASK 0x00000700 /* When set, forces corresponding input to */ + /* phase track the previous input. */ + /* I2S0 can phase track the last S/PDIF input */ +#define HCFG_I2S_ASRC_ENABLE 0x00000070 /* When set, enables asynchronous sample rate */ + /* conversion for the corresponding */ + /* I2S format input */ +/* Rest of HCFG 0x0000000f same as below. LOCKSOUNDCACHE etc. */ + + + +/* Older chips */ #define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ #define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ #define HCFG_GPINPUT0 0x00004000 /* External pin112 */ @@ -886,6 +914,280 @@ #define A_HIWORD_RESULT_MASK 0x007ff000 #define A_HIWORD_OPA_MASK 0x000007ff +/************************************************************************************************/ +/* EMU1010m HANA FPGA registers */ +/************************************************************************************************/ +#define EMU_HANA_DESTHI 0x00 /* 0000xxx 3 bits Link Destination */ +#define EMU_HANA_DESTLO 0x01 /* 00xxxxx 5 bits */ +#define EMU_HANA_SRCHI 0x02 /* 0000xxx 3 bits Link Source */ +#define EMU_HANA_SRCLO 0x03 /* 00xxxxx 5 bits */ +#define EMU_HANA_DOCK_PWR 0x04 /* 000000x 1 bits Audio Dock power */ +#define EMU_HANA_DOCK_PWR_ON 0x01 /* Audio Dock power on */ +#define EMU_HANA_WCLOCK 0x05 /* 0000xxx 3 bits Word Clock source select */ + /* Must be written after power on to reset DLL */ + /* One is unable to detect the Audio dock without this */ +#define EMU_HANA_WCLOCK_SRC_MASK 0x07 +#define EMU_HANA_WCLOCK_INT_48K 0x00 +#define EMU_HANA_WCLOCK_INT_44_1K 0x01 +#define EMU_HANA_WCLOCK_HANA_SPDIF_IN 0x02 +#define EMU_HANA_WCLOCK_HANA_ADAT_IN 0x03 +#define EMU_HANA_WCLOCK_SYNC_BNCN 0x04 +#define EMU_HANA_WCLOCK_2ND_HANA 0x05 +#define EMU_HANA_WCLOCK_SRC_RESERVED 0x06 +#define EMU_HANA_WCLOCK_OFF 0x07 /* For testing, forces fallback to DEFCLOCK */ +#define EMU_HANA_WCLOCK_MULT_MASK 0x18 +#define EMU_HANA_WCLOCK_1X 0x00 +#define EMU_HANA_WCLOCK_2X 0x08 +#define EMU_HANA_WCLOCK_4X 0x10 +#define EMU_HANA_WCLOCK_MULT_RESERVED 0x18 + +#define EMU_HANA_DEFCLOCK 0x06 /* 000000x 1 bits Default Word Clock */ +#define EMU_HANA_DEFCLOCK_48K 0x00 +#define EMU_HANA_DEFCLOCK_44_1K 0x01 + +#define EMU_HANA_UNMUTE 0x07 /* 000000x 1 bits Mute all audio outputs */ +#define EMU_MUTE 0x00 +#define EMU_UNMUTE 0x01 + +#define EMU_HANA_FPGA_CONFIG 0x08 /* 00000xx 2 bits Config control of FPGAs */ +#define EMU_HANA_FPGA_CONFIG_AUDIODOCK 0x01 /* Set in order to program FPGA on Audio Dock */ +#define EMU_HANA_FPGA_CONFIG_HANA 0x02 /* Set in order to program FPGA on Hana */ + +#define EMU_HANA_IRQ_ENABLE 0x09 /* 000xxxx 4 bits IRQ Enable */ +#define EMU_HANA_IRQ_WCLK_CHANGED 0x01 +#define EMU_HANA_IRQ_ADAT 0x02 +#define EMU_HANA_IRQ_DOCK 0x04 +#define EMU_HANA_IRQ_DOCK_LOST 0x08 + +#define EMU_HANA_SPDIF_MODE 0x0a /* 00xxxxx 5 bits SPDIF MODE */ +#define EMU_HANA_SPDIF_MODE_TX_COMSUMER 0x00 +#define EMU_HANA_SPDIF_MODE_TX_PRO 0x01 +#define EMU_HANA_SPDIF_MODE_TX_NOCOPY 0x02 +#define EMU_HANA_SPDIF_MODE_RX_COMSUMER 0x00 +#define EMU_HANA_SPDIF_MODE_RX_PRO 0x04 +#define EMU_HANA_SPDIF_MODE_RX_NOCOPY 0x08 +#define EMU_HANA_SPDIF_MODE_RX_INVALID 0x10 + +#define EMU_HANA_OPTICAL_TYPE 0x0b /* 00000xx 2 bits ADAT or SPDIF in/out */ +#define EMU_HANA_OPTICAL_IN_SPDIF 0x00 +#define EMU_HANA_OPTICAL_IN_ADAT 0x01 +#define EMU_HANA_OPTICAL_OUT_SPDIF 0x00 +#define EMU_HANA_OPTICAL_OUT_ADAT 0x02 + +#define EMU_HANA_MIDI 0x0c /* 000000x 1 bit Control MIDI */ +#define EMU_HANA_MIDI_IN_FROM_HAMOA 0x00 /* HAMOA MIDI in to Alice 2 MIDI B */ +#define EMU_HANA_MIDI_IN_FROM_DOCK 0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */ + +#define EMU_HANA_DOCK_LEDS_1 0x0d /* 000xxxx 4 bit Audio Dock LEDs */ +#define EMU_HANA_DOCK_LEDS_1_MIDI1 0x01 /* MIDI 1 LED on */ +#define EMU_HANA_DOCK_LEDS_1_MIDI2 0x02 /* MIDI 2 LED on */ +#define EMU_HANA_DOCK_LEDS_1_SMPTE_IN 0x04 /* SMPTE IN LED on */ +#define EMU_HANA_DOCK_LEDS_1_SMPTE_OUT 0x08 /* SMPTE OUT LED on */ + +#define EMU_HANA_DOCK_LEDS_2 0x0e /* 0xxxxxx 6 bit Audio Dock LEDs */ +#define EMU_HANA_DOCK_LEDS_2_44K 0x01 /* 44.1 kHz LED on */ +#define EMU_HANA_DOCK_LEDS_2_48K 0x02 /* 48 kHz LED on */ +#define EMU_HANA_DOCK_LEDS_2_96K 0x04 /* 96 kHz LED on */ +#define EMU_HANA_DOCK_LEDS_2_192K 0x08 /* 192 kHz LED on */ +#define EMU_HANA_DOCK_LEDS_2_LOCK 0x10 /* LOCK LED on */ +#define EMU_HANA_DOCK_LEDS_2_EXT 0x20 /* EXT LED on */ + +#define EMU_HANA_DOCK_LEDS_3 0x0f /* 0xxxxxx 6 bit Audio Dock LEDs */ +#define EMU_HANA_DOCK_LEDS_3_CLIP_A 0x01 /* Mic A Clip LED on */ +#define EMU_HANA_DOCK_LEDS_3_CLIP_B 0x02 /* Mic B Clip LED on */ +#define EMU_HANA_DOCK_LEDS_3_SIGNAL_A 0x04 /* Signal A Clip LED on */ +#define EMU_HANA_DOCK_LEDS_3_SIGNAL_B 0x08 /* Signal B Clip LED on */ +#define EMU_HANA_DOCK_LEDS_3_MANUAL_CLIP 0x10 /* Manual Clip detection */ +#define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL 0x20 /* Manual Signal detection */ + +#define EMU_HANA_DOCK_PADS 0x10 /* 0000xxx 3 bit Audio Dock ADC 14dB pads */ +#define EMU_HANA_DOCK_PAD1 0x01 /* 14dB Attenuation on ADC 1 */ +#define EMU_HANA_DOCK_PAD2 0x02 /* 14dB Attenuation on ADC 2 */ +#define EMU_HANA_DOCK_PAD3 0x04 /* 14dB Attenuation on ADC 3 */ + +#define EMU_HANA_DOCK_MISC 0x11 /* 0xxxxxx 6 bit Audio Dock misc bits */ +#define EMU_HANA_DOCK_DAC1_MUTE 0x01 /* DAC 1 Mute */ +#define EMU_HANA_DOCK_DAC2_MUTE 0x02 /* DAC 2 Mute */ +#define EMU_HANA_DOCK_DAC3_MUTE 0x04 /* DAC 3 Mute */ +#define EMU_HANA_DOCK_DAC4_MUTE 0x08 /* DAC 4 Mute */ +#define EMU_HANA_DOCK_PHONES_192_DAC1 0x00 /* DAC 1 Headphones source at 192kHz */ +#define EMU_HANA_DOCK_PHONES_192_DAC2 0x10 /* DAC 2 Headphones source at 192kHz */ +#define EMU_HANA_DOCK_PHONES_192_DAC3 0x20 /* DAC 3 Headphones source at 192kHz */ +#define EMU_HANA_DOCK_PHONES_192_DAC4 0x30 /* DAC 4 Headphones source at 192kHz */ + +#define EMU_HANA_UNKNOWN12 0x12 /* 0xxxxxx 6 bit Unknown12 */ +#define EMU_HANA_UNKNOWN13 0x13 /* 0xxxxxx 6 bit Unknown13 */ +/* 0x14 - 0x1f Unused R/W registers */ +#define EMU_HANA_IRQ_STATUS 0x20 /* 000xxxx 4 bits IRQ Status */ +#if 0 /* Already defined for reg 0x09 IRQ_ENABLE */ +#define EMU_HANA_IRQ_WCLK_CHANGED 0x01 +#define EMU_HANA_IRQ_ADAT 0x02 +#define EMU_HANA_IRQ_DOCK 0x04 +#define EMU_HANA_IRQ_DOCK_LOST 0x08 +#endif + +#define EMU_HANA_OPTION_CARDS 0x21 /* 000xxxx 4 bits Presence of option cards */ +#define EMU_HANA_OPTION_HAMOA 0x01 /* HAMOA card present */ +#define EMU_HANA_OPTION_SYNC 0x02 /* Sync card present */ +#define EMU_HANA_OPTION_DOCK_ONLINE 0x04 /* Audio Dock online and FPGA configured */ +#define EMU_HANA_OPTION_DOCK_OFFLINE 0x08 /* Audio Dock online and FPGA not configured */ + +#define EMU_HANA_ID 0x22 /* 1010101 7 bits ID byte & 0x7f = 0x55 */ + +#define EMU_HANA_MAJOR_REV 0x23 /* 0000xxx 3 bit Hana FPGA Major rev */ +#define EMU_HANA_MINOR_REV 0x24 /* 0000xxx 3 bit Hana FPGA Minor rev */ + +#define EMU_DOCK_MAJOR_REV 0x25 /* 0000xxx 3 bit Audio Dock FPGA Major rev */ +#define EMU_DOCK_MINOR_REV 0x26 /* 0000xxx 3 bit Audio Dock FPGA Minor rev */ + +#define EMU_DOCK_BOARD_ID 0x27 /* 00000xx 2 bits Audio Dock ID pins */ +#define EMU_DOCK_BOARD_ID0 0x00 /* ID bit 0 */ +#define EMU_DOCK_BOARD_ID1 0x03 /* ID bit 1 */ + +#define EMU_HANA_WC_SPDIF_HI 0x28 /* 0xxxxxx 6 bit SPDIF IN Word clock, upper 6 bits */ +#define EMU_HANA_WC_SPDIF_LO 0x29 /* 0xxxxxx 6 bit SPDIF IN Word clock, lower 6 bits */ + +#define EMU_HANA_WC_ADAT_HI 0x2a /* 0xxxxxx 6 bit ADAT IN Word clock, upper 6 bits */ +#define EMU_HANA_WC_ADAT_LO 0x2b /* 0xxxxxx 6 bit ADAT IN Word clock, lower 6 bits */ + +#define EMU_HANA_WC_BNC_LO 0x2c /* 0xxxxxx 6 bit BNC IN Word clock, lower 6 bits */ +#define EMU_HANA_WC_BNC_HI 0x2d /* 0xxxxxx 6 bit BNC IN Word clock, upper 6 bits */ + +#define EMU_HANA2_WC_SPDIF_HI 0x2e /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, upper 6 bits */ +#define EMU_HANA2_WC_SPDIF_LO 0x2f /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, lower 6 bits */ +/* 0x30 - 0x3f Unused Read only registers */ + +/************************************************************************************************/ +/* EMU1010m HANA Destinations */ +/************************************************************************************************/ +#define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_3 0x0002 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_4 0x0003 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_5 0x0004 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_6 0x0005 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_7 0x0006 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_8 0x0007 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_9 0x0008 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_A 0x0009 /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_B 0x000a /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_C 0x000b /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_D 0x000c /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_E 0x000d /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_ALICE2_EMU32_F 0x000e /* 16 EMU32 channels to Alice2 +0 to +0xf */ +#define EMU_DST_DOCK_DAC1_LEFT1 0x0100 /* Audio Dock DAC1 Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC1_LEFT2 0x0101 /* Audio Dock DAC1 Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC1_LEFT3 0x0102 /* Audio Dock DAC1 Left, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC1_LEFT4 0x0103 /* Audio Dock DAC1 Left, 4th or 192kHz */ +#define EMU_DST_DOCK_DAC1_RIGHT1 0x0104 /* Audio Dock DAC1 Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC1_RIGHT2 0x0105 /* Audio Dock DAC1 Right, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC1_RIGHT3 0x0106 /* Audio Dock DAC1 Right, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC1_RIGHT4 0x0107 /* Audio Dock DAC1 Right, 4th or 192kHz */ +#define EMU_DST_DOCK_DAC2_LEFT1 0x0108 /* Audio Dock DAC2 Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC2_LEFT2 0x0109 /* Audio Dock DAC2 Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC2_LEFT3 0x010a /* Audio Dock DAC2 Left, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC2_LEFT4 0x010b /* Audio Dock DAC2 Left, 4th or 192kHz */ +#define EMU_DST_DOCK_DAC2_RIGHT1 0x010c /* Audio Dock DAC2 Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC2_RIGHT2 0x010d /* Audio Dock DAC2 Right, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC2_RIGHT3 0x010e /* Audio Dock DAC2 Right, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC2_RIGHT4 0x010f /* Audio Dock DAC2 Right, 4th or 192kHz */ +#define EMU_DST_DOCK_DAC3_LEFT1 0x0110 /* Audio Dock DAC1 Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC3_LEFT2 0x0111 /* Audio Dock DAC1 Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC3_LEFT3 0x0112 /* Audio Dock DAC1 Left, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC3_LEFT4 0x0113 /* Audio Dock DAC1 Left, 4th or 192kHz */ +#define EMU_DST_DOCK_PHONES_LEFT1 0x0112 /* Audio Dock PHONES Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_PHONES_LEFT2 0x0113 /* Audio Dock PHONES Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC3_RIGHT1 0x0114 /* Audio Dock DAC1 Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC3_RIGHT2 0x0115 /* Audio Dock DAC1 Right, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC3_RIGHT3 0x0116 /* Audio Dock DAC1 Right, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC3_RIGHT4 0x0117 /* Audio Dock DAC1 Right, 4th or 192kHz */ +#define EMU_DST_DOCK_PHONES_RIGHT1 0x0116 /* Audio Dock PHONES Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_PHONES_RIGHT2 0x0117 /* Audio Dock PHONES Right, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC4_LEFT1 0x0118 /* Audio Dock DAC2 Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC4_LEFT2 0x0119 /* Audio Dock DAC2 Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC4_LEFT3 0x011a /* Audio Dock DAC2 Left, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC4_LEFT4 0x011b /* Audio Dock DAC2 Left, 4th or 192kHz */ +#define EMU_DST_DOCK_SPDIF_LEFT1 0x011a /* Audio Dock SPDIF Left, 1st or 48kHz only */ +#define EMU_DST_DOCK_SPDIF_LEFT2 0x011b /* Audio Dock SPDIF Left, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC4_RIGHT1 0x011c /* Audio Dock DAC2 Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_DAC4_RIGHT2 0x011d /* Audio Dock DAC2 Right, 2nd or 96kHz */ +#define EMU_DST_DOCK_DAC4_RIGHT3 0x011e /* Audio Dock DAC2 Right, 3rd or 192kHz */ +#define EMU_DST_DOCK_DAC4_RIGHT4 0x011f /* Audio Dock DAC2 Right, 4th or 192kHz */ +#define EMU_DST_DOCK_SPDIF_RIGHT1 0x011e /* Audio Dock SPDIF Right, 1st or 48kHz only */ +#define EMU_DST_DOCK_SPDIF_RIGHT2 0x011f /* Audio Dock SPDIF Right, 2nd or 96kHz */ +#define EMU_DST_HANA_SPDIF_LEFT1 0x0200 /* Hana SPDIF Left, 1st or 48kHz only */ +#define EMU_DST_HANA_SPDIF_LEFT2 0x0202 /* Hana SPDIF Left, 2nd or 96kHz */ +#define EMU_DST_HANA_SPDIF_RIGHT1 0x0201 /* Hana SPDIF Right, 1st or 48kHz only */ +#define EMU_DST_HANA_SPDIF_RIGHT2 0x0203 /* Hana SPDIF Right, 2nd or 96kHz */ +#define EMU_DST_HAMOA_DAC_LEFT1 0x0300 /* Hamoa DAC Left, 1st or 48kHz only */ +#define EMU_DST_HAMOA_DAC_LEFT2 0x0302 /* Hamoa DAC Left, 2nd or 96kHz */ +#define EMU_DST_HAMOA_DAC_LEFT3 0x0304 /* Hamoa DAC Left, 3rd or 192kHz */ +#define EMU_DST_HAMOA_DAC_LEFT4 0x0306 /* Hamoa DAC Left, 4th or 192kHz */ +#define EMU_DST_HAMOA_DAC_RIGHT1 0x0301 /* Hamoa DAC Right, 1st or 48kHz only */ +#define EMU_DST_HAMOA_DAC_RIGHT2 0x0303 /* Hamoa DAC Right, 2nd or 96kHz */ +#define EMU_DST_HAMOA_DAC_RIGHT3 0x0305 /* Hamoa DAC Right, 3rd or 192kHz */ +#define EMU_DST_HAMOA_DAC_RIGHT4 0x0307 /* Hamoa DAC Right, 4th or 192kHz */ +#define EMU_DST_HANA_ADAT 0x0400 /* Hana ADAT 8 channel out +0 to +7 */ +#define EMU_DST_ALICE_I2S0_LEFT 0x0500 /* Alice2 I2S0 Left */ +#define EMU_DST_ALICE_I2S0_RIGHT 0x0501 /* Alice2 I2S0 Right */ +#define EMU_DST_ALICE_I2S1_LEFT 0x0600 /* Alice2 I2S1 Left */ +#define EMU_DST_ALICE_I2S1_RIGHT 0x0601 /* Alice2 I2S1 Right */ +#define EMU_DST_ALICE_I2S2_LEFT 0x0700 /* Alice2 I2S2 Left */ +#define EMU_DST_ALICE_I2S2_RIGHT 0x0701 /* Alice2 I2S2 Right */ + +/************************************************************************************************/ +/* EMU1010m HANA Sources */ +/************************************************************************************************/ +#define EMU_SRC_SILENCE 0x0000 /* Silence */ +#define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */ +#define EMU_SRC_DOCK_MIC_A2 0x0101 /* Audio Dock Mic A, 2nd or 96kHz */ +#define EMU_SRC_DOCK_MIC_A3 0x0102 /* Audio Dock Mic A, 3rd or 192kHz */ +#define EMU_SRC_DOCK_MIC_A4 0x0103 /* Audio Dock Mic A, 4th or 192kHz */ +#define EMU_SRC_DOCK_MIC_B1 0x0104 /* Audio Dock Mic B, 1st or 48kHz only */ +#define EMU_SRC_DOCK_MIC_B2 0x0105 /* Audio Dock Mic B, 2nd or 96kHz */ +#define EMU_SRC_DOCK_MIC_B3 0x0106 /* Audio Dock Mic B, 3rd or 192kHz */ +#define EMU_SRC_DOCK_MIC_B4 0x0107 /* Audio Dock Mic B, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC1_LEFT1 0x0108 /* Audio Dock ADC1 Left, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC1_LEFT2 0x0109 /* Audio Dock ADC1 Left, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC1_LEFT3 0x010a /* Audio Dock ADC1 Left, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC1_LEFT4 0x010b /* Audio Dock ADC1 Left, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC1_RIGHT1 0x010c /* Audio Dock ADC1 Right, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC1_RIGHT2 0x010d /* Audio Dock ADC1 Right, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC1_RIGHT3 0x010e /* Audio Dock ADC1 Right, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC1_RIGHT4 0x010f /* Audio Dock ADC1 Right, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC2_LEFT1 0x0110 /* Audio Dock ADC2 Left, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC2_LEFT2 0x0111 /* Audio Dock ADC2 Left, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC2_LEFT3 0x0112 /* Audio Dock ADC2 Left, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC2_LEFT4 0x0113 /* Audio Dock ADC2 Left, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC2_RIGHT1 0x0114 /* Audio Dock ADC2 Right, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC2_RIGHT2 0x0115 /* Audio Dock ADC2 Right, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC2_RIGHT3 0x0116 /* Audio Dock ADC2 Right, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC2_RIGHT4 0x0117 /* Audio Dock ADC2 Right, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC3_LEFT1 0x0118 /* Audio Dock ADC3 Left, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC3_LEFT2 0x0119 /* Audio Dock ADC3 Left, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC3_LEFT3 0x011a /* Audio Dock ADC3 Left, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC3_LEFT4 0x011b /* Audio Dock ADC3 Left, 4th or 192kHz */ +#define EMU_SRC_DOCK_ADC3_RIGHT1 0x011c /* Audio Dock ADC3 Right, 1st or 48kHz only */ +#define EMU_SRC_DOCK_ADC3_RIGHT2 0x011d /* Audio Dock ADC3 Right, 2nd or 96kHz */ +#define EMU_SRC_DOCK_ADC3_RIGHT3 0x011e /* Audio Dock ADC3 Right, 3rd or 192kHz */ +#define EMU_SRC_DOCK_ADC3_RIGHT4 0x011f /* Audio Dock ADC3 Right, 4th or 192kHz */ +#define EMU_SRC_HAMOA_ADC_LEFT1 0x0200 /* Hamoa ADC Left, 1st or 48kHz only */ +#define EMU_SRC_HAMOA_ADC_LEFT2 0x0202 /* Hamoa ADC Left, 2nd or 96kHz */ +#define EMU_SRC_HAMOA_ADC_LEFT3 0x0204 /* Hamoa ADC Left, 3rd or 192kHz */ +#define EMU_SRC_HAMOA_ADC_LEFT4 0x0206 /* Hamoa ADC Left, 4th or 192kHz */ +#define EMU_SRC_HAMOA_ADC_RIGHT1 0x0201 /* Hamoa ADC Right, 1st or 48kHz only */ +#define EMU_SRC_HAMOA_ADC_RIGHT2 0x0203 /* Hamoa ADC Right, 2nd or 96kHz */ +#define EMU_SRC_HAMOA_ADC_RIGHT3 0x0205 /* Hamoa ADC Right, 3rd or 192kHz */ +#define EMU_SRC_HAMOA_ADC_RIGHT4 0x0207 /* Hamoa ADC Right, 4th or 192kHz */ +#define EMU_SRC_ALICE_EMU32A 0x0300 /* Alice2 EMU32a 16 outputs. +0 to +0xf */ +#define EMU_SRC_ALICE_EMU32B 0x0310 /* Alice2 EMU32b 16 outputs. +0 to +0xf */ +#define EMU_SRC_HANA_ADAT 0x0400 /* Hana ADAT 8 channel in +0 to +7 */ +#define EMU_SRC_HANA_SPDIF_LEFT1 0x0500 /* Hana SPDIF Left, 1st or 48kHz only */ +#define EMU_SRC_HANA_SPDIF_LEFT2 0x0502 /* Hana SPDIF Left, 2nd or 96kHz */ +#define EMU_SRC_HANA_SPDIF_RIGHT1 0x0501 /* Hana SPDIF Right, 1st or 48kHz only */ +#define EMU_SRC_HANA_SPDIF_RIGHT2 0x0503 /* Hana SPDIF Right, 2nd or 96kHz */ +/* 0x600 and 0x700 no used */ /* ------------------- STRUCTURES -------------------- */ @@ -1063,7 +1365,7 @@ struct snd_emu_chip_details { unsigned char spdif_bug; /* Has Spdif phasing bug */ unsigned char ac97_chip; /* Has an AC97 chip: 1 = mandatory, 2 = optional */ unsigned char ecard; /* APS EEPROM */ - unsigned char emu1212m; /* EMU 1212m card */ + unsigned char emu1010; /* EMU 1010m card */ unsigned char spi_dac; /* SPI interface for DAC */ unsigned char i2c_adc; /* I2C interface for ADC */ unsigned char adc_1361t; /* Use Philips 1361T ADC */ @@ -1072,6 +1374,11 @@ struct snd_emu_chip_details { const char *id; /* for backward compatibility - can be NULL if not needed */ }; +struct snd_emu1010 { + unsigned int output_source[64]; + unsigned int input_source[64]; +}; + struct snd_emu10k1 { int irq; @@ -1132,6 +1439,7 @@ struct snd_emu10k1 { int p16v_device_offset; u32 p16v_capture_source; u32 p16v_capture_channel; + struct snd_emu1010 emu1010; struct snd_emu10k1_pcm_mixer pcm_mixer[32]; struct snd_emu10k1_pcm_mixer efx_pcm_mixer[NUM_EFX_PLAYBACK]; struct snd_kcontrol *ctl_send_routing; @@ -1208,6 +1516,9 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn); void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data); int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data); +int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value); +int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value); +int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src); unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc); void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb); void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 972ec40d8166..891172f2b1d7 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -3,8 +3,10 @@ * Creative Labs, Inc. * Routines for control of EMU10K1 chips * - * Copyright (c) by James Courtier-Dutton + * Copyright (c) by James Courtier-Dutton * Added support for Audigy 2 Value. + * Added EMU 1010 support. + * General bug fixes and enhancements. * * * BUGS: @@ -41,6 +43,7 @@ #include #include +#include #include "p16v.h" #include "tina2.h" @@ -211,7 +214,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) int size, n; size = ARRAY_SIZE(spi_dac_init); - for (n=0; n < size; n++) + for (n = 0; n < size; n++) snd_emu10k1_spi_write(emu, spi_dac_init[n]); snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10); @@ -239,6 +242,10 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); } + if (emu->card_capabilities->emu1010) { + outl(HCFG_AUTOMUTE_ASYNC | + HCFG_EMU32_SLAVE | + HCFG_AUDIOENABLE, emu->port + HCFG); /* * Hokay, setup HCFG * Mute Disable Audio = 0 @@ -246,7 +253,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) * Lock Sound Memory = 0 * Auto Mute = 1 */ - if (emu->audigy) { + } else if (emu->audigy) { if (emu->revision == 4) /* audigy2 */ outl(HCFG_AUDIOENABLE | HCFG_AC3ENABLE_CDSPDIF | @@ -265,8 +272,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); if (enable_ir) { /* enable IR for SB Live */ - if ( emu->card_capabilities->emu1212m) { - ; /* Disable all access to A_IOCFG for the emu1212m */ + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->audigy) { unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); @@ -284,8 +291,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } } - if ( emu->card_capabilities->emu1212m) { - ; /* Disable all access to A_IOCFG for the emu1212m */ + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->audigy) { /* enable analog output */ unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); @@ -302,8 +309,8 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); /* Enable analog/digital outs on audigy */ - if ( emu->card_capabilities->emu1212m) { - ; /* Disable all access to A_IOCFG for the emu1212m */ + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->audigy) { outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); @@ -596,133 +603,417 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu) return 0; } -static int snd_emu1212m_fpga_write(struct snd_emu10k1 * emu, int reg, int value) -{ - if (reg<0 || reg>0x3f) - return 1; - reg+=0x40; /* 0x40 upwards are registers. */ - if (value<0 || value>0x3f) /* 0 to 0x3f are values */ - return 1; - outl(reg, emu->port + A_IOCFG); - outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ - outl(value, emu->port + A_IOCFG); - outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ - - return 0; -} - -static int snd_emu1212m_fpga_read(struct snd_emu10k1 * emu, int reg, int *value) +static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename) { - if (reg<0 || reg>0x3f) - return 1; - reg+=0x40; /* 0x40 upwards are registers. */ - outl(reg, emu->port + A_IOCFG); - outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ - *value = inl(emu->port + A_IOCFG); - - return 0; -} + int err; + int n, i; + int reg; + int value; + const struct firmware *fw_entry; + + if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) { + snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err); + return err; + } + snd_printk(KERN_INFO "firmware size=0x%x\n",fw_entry->size); + if (fw_entry->size != 0x133a4) { + snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); + return -EINVAL; + } -static int snd_emu1212m_fpga_netlist_write(struct snd_emu10k1 * emu, int reg, int value) -{ - snd_emu1212m_fpga_write(emu, 0x00, ((reg >> 8) & 0x3f) ); - snd_emu1212m_fpga_write(emu, 0x01, (reg & 0x3f) ); - snd_emu1212m_fpga_write(emu, 0x02, ((value >> 8) & 0x3f) ); - snd_emu1212m_fpga_write(emu, 0x03, (value & 0x3f) ); + /* The FPGA is a Xilinx Spartan IIE XC2S50E */ + /* GPIO7 -> FPGA PGMN + * GPIO6 -> FPGA CCLK + * GPIO5 -> FPGA DIN + * FPGA CONFIG OFF -> FPGA PGMN + */ + outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ + udelay(1); + outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ + udelay(100); /* Allow FPGA memory to clean */ + for(n = 0; n < fw_entry->size; n++) { + value=fw_entry->data[n]; + for(i = 0; i < 8; i++) { + reg = 0x80; + if (value & 0x1) + reg = reg | 0x20; + value = value >> 1; + outl(reg, emu->port + A_IOCFG); + outl(reg | 0x40, emu->port + A_IOCFG); + } + } + /* After programming, set GPIO bit 4 high again. */ + outl(0x10, emu->port + A_IOCFG); + + release_firmware(fw_entry); return 0; } -static int snd_emu10k1_emu1212m_init(struct snd_emu10k1 * emu) +static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) { unsigned int i; - int tmp; - - snd_printk(KERN_ERR "emu1212m: Special config.\n"); + int tmp,tmp2; + int reg; + int err; + const char *hana_filename = "emu/hana.fw"; + const char *dock_filename = "emu/audio_dock.fw"; + + snd_printk(KERN_INFO "emu1010: Special config.\n"); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Mute all codecs. + */ outl(0x0005a00c, emu->port + HCFG); - outl(0x0005a004, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Lock Tank Memory Cache, + * Mute all codecs. + */ + outl(0x0005a004, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Mute all codecs. + */ outl(0x0005a000, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Mute all codecs. + */ outl(0x0005a000, emu->port + HCFG); - snd_emu1212m_fpga_read(emu, 0x22, &tmp ); - snd_emu1212m_fpga_read(emu, 0x23, &tmp ); - snd_emu1212m_fpga_read(emu, 0x24, &tmp ); - snd_emu1212m_fpga_write(emu, 0x04, 0x01 ); - snd_emu1212m_fpga_read(emu, 0x0b, &tmp ); - snd_emu1212m_fpga_write(emu, 0x0b, 0x01 ); - snd_emu1212m_fpga_read(emu, 0x10, &tmp ); - snd_emu1212m_fpga_write(emu, 0x10, 0x00 ); - snd_emu1212m_fpga_read(emu, 0x11, &tmp ); - snd_emu1212m_fpga_write(emu, 0x11, 0x30 ); - snd_emu1212m_fpga_read(emu, 0x13, &tmp ); - snd_emu1212m_fpga_write(emu, 0x13, 0x0f ); - snd_emu1212m_fpga_read(emu, 0x11, &tmp ); - snd_emu1212m_fpga_write(emu, 0x11, 0x30 ); - snd_emu1212m_fpga_read(emu, 0x0a, &tmp ); - snd_emu1212m_fpga_write(emu, 0x0a, 0x10 ); - snd_emu1212m_fpga_write(emu, 0x0c, 0x19 ); - snd_emu1212m_fpga_write(emu, 0x12, 0x0c ); - snd_emu1212m_fpga_write(emu, 0x09, 0x0f ); - snd_emu1212m_fpga_write(emu, 0x06, 0x00 ); - snd_emu1212m_fpga_write(emu, 0x05, 0x00 ); - snd_emu1212m_fpga_write(emu, 0x0e, 0x12 ); - snd_emu1212m_fpga_netlist_write(emu, 0x0000, 0x0200); - snd_emu1212m_fpga_netlist_write(emu, 0x0001, 0x0201); - snd_emu1212m_fpga_netlist_write(emu, 0x0002, 0x0500); - snd_emu1212m_fpga_netlist_write(emu, 0x0003, 0x0501); - snd_emu1212m_fpga_netlist_write(emu, 0x0004, 0x0400); - snd_emu1212m_fpga_netlist_write(emu, 0x0005, 0x0401); - snd_emu1212m_fpga_netlist_write(emu, 0x0006, 0x0402); - snd_emu1212m_fpga_netlist_write(emu, 0x0007, 0x0403); - snd_emu1212m_fpga_netlist_write(emu, 0x0008, 0x0404); - snd_emu1212m_fpga_netlist_write(emu, 0x0009, 0x0405); - snd_emu1212m_fpga_netlist_write(emu, 0x000a, 0x0406); - snd_emu1212m_fpga_netlist_write(emu, 0x000b, 0x0407); - snd_emu1212m_fpga_netlist_write(emu, 0x000c, 0x0100); - snd_emu1212m_fpga_netlist_write(emu, 0x000d, 0x0104); - snd_emu1212m_fpga_netlist_write(emu, 0x000e, 0x0200); - snd_emu1212m_fpga_netlist_write(emu, 0x000f, 0x0201); - for (i=0;i < 0x20;i++) { - snd_emu1212m_fpga_netlist_write(emu, 0x0100+i, 0x0000); + /* Disable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); + + /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printdd("reg1=0x%x\n",reg); + if (reg == 0x55) { + /* FPGA netlist already present so clear it */ + /* Return to programming mode */ + + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02 ); } - for (i=0;i < 4;i++) { - snd_emu1212m_fpga_netlist_write(emu, 0x0200+i, 0x0000); + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printdd("reg2=0x%x\n",reg); + if (reg == 0x55) { + /* FPGA failed to return to programming mode */ + return -ENODEV; } - for (i=0;i < 7;i++) { - snd_emu1212m_fpga_netlist_write(emu, 0x0300+i, 0x0000); + snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); + if ((err = snd_emu1010_load_firmware(emu, hana_filename)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", hana_filename); + return err; } - for (i=0;i < 7;i++) { - snd_emu1212m_fpga_netlist_write(emu, 0x0400+i, 0x0000); + + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + if (reg != 0x55) { + /* FPGA failed to be programmed */ + snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg); + return -ENODEV; } - snd_emu1212m_fpga_netlist_write(emu, 0x0500, 0x0108); - snd_emu1212m_fpga_netlist_write(emu, 0x0501, 0x010c); - snd_emu1212m_fpga_netlist_write(emu, 0x0600, 0x0110); - snd_emu1212m_fpga_netlist_write(emu, 0x0601, 0x0114); - snd_emu1212m_fpga_netlist_write(emu, 0x0700, 0x0118); - snd_emu1212m_fpga_netlist_write(emu, 0x0701, 0x011c); - snd_emu1212m_fpga_write(emu, 0x07, 0x01 ); - snd_emu1212m_fpga_read(emu, 0x21, &tmp ); + snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp ); + snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 ); + snd_printk("Hana ver:%d.%d\n",tmp ,tmp2); + /* Enable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON ); + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg); + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg); + snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); + /* ADAT input. */ + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 ); + snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_PADS, &tmp ); + /* Set no attenuation on Audio Dock pads. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PADS, 0x00 ); + snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); + /* Unmute Audio dock DACs, Headphone source DAC-4. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 ); + snd_emu1010_fpga_read(emu, EMU_HANA_UNKNOWN13, &tmp ); + /* Unknown. */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN13, 0x0f ); + snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); + snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); + /* SPDIF Format. Set Consumer mode, 24bit, copy enable */ + snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); + /* MIDI routing */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); + /* Unknown. */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); + /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */ + /* IRQ Enable: All off */ + snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 ); + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg); + /* Default WCLK set to 48kHz. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 ); + /* Word Clock source, Internal 48kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K ); + //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X ); + /* Audio Dock LEDs. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 ); - outl(0x0000a000, emu->port + HCFG); +#if 0 + /* For 96kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2); +#endif +#if 0 + /* For 192kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4); +#endif +#if 1 + /* For 48kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1); +#endif +#if 0 + /* Original */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2); +#endif + for (i = 0;i < 0x20; i++ ) { + /* AudioDock Elink <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 4; i++) { + /* Hana SPDIF Out <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 7; i++) { + /* Hamoa DAC <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 7; i++) { + /* Hana ADAT Out <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE); + } + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1); + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp ); + + /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Mute all codecs. + */ + outl(0x0000a000, emu->port + HCFG); + /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Un-Mute all codecs. + */ outl(0x0000a001, emu->port + HCFG); + /* Initial boot complete. Now patches */ - snd_emu1212m_fpga_read(emu, 0x21, &tmp ); - snd_emu1212m_fpga_write(emu, 0x0c, 0x19 ); - snd_emu1212m_fpga_write(emu, 0x12, 0x0c ); - snd_emu1212m_fpga_write(emu, 0x0c, 0x19 ); - snd_emu1212m_fpga_write(emu, 0x12, 0x0c ); - snd_emu1212m_fpga_read(emu, 0x0a, &tmp ); - snd_emu1212m_fpga_write(emu, 0x0a, 0x10 ); - - snd_emu1212m_fpga_read(emu, 0x20, &tmp ); - snd_emu1212m_fpga_read(emu, 0x21, &tmp ); - - snd_emu1212m_fpga_netlist_write(emu, 0x0300, 0x0312); - snd_emu1212m_fpga_netlist_write(emu, 0x0301, 0x0313); - snd_emu1212m_fpga_netlist_write(emu, 0x0200, 0x0302); - snd_emu1212m_fpga_netlist_write(emu, 0x0201, 0x0303); + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); /* Unknown */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); /* Unknown */ + snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ + + /* Delay to allow Audio Dock to settle */ + msleep(100); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); /* OPTIONS: Which cards are attached to the EMU */ + /* FIXME: The loading of this should be able to happen any time, + * as the user can plug/unplug it at any time + */ + if (reg & (EMU_HANA_OPTION_DOCK_ONLINE | EMU_HANA_OPTION_DOCK_OFFLINE) ) { + /* Audio Dock attached */ + /* Return to Audio Dock programming mode */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); + if ((err = snd_emu1010_load_firmware(emu, dock_filename)) != 0) { + return err; + } + snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg); + if (reg != 0x55) { + /* FPGA failed to be programmed */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); + return 0; + return -ENODEV; + } + } +#if 0 + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */ +#endif + /* Default outputs */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[0] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[6] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[7] = 28; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[8] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[9] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[10] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[11] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[12] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[13] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[14] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[15] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[16] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[18] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[19] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[20] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[21] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[22] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[23] = 28; + + /* TEMP: Select SPDIF in/out */ + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ + + /* TEMP: Select 48kHz SPDIF out */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */ + /* Word Clock source, Internal 48kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K ); + //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X ); + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */ + //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */ + //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */ + //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */ return 0; } @@ -747,6 +1038,10 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } snd_emu10k1_free_efx(emu); } + if (emu->card_capabilities->emu1010) { + /* Disable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); + } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) @@ -865,11 +1160,12 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ac97_chip = 1} , /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, - .driver = "Audigy2", .name = "E-mu 1212m [4001]", - .id = "EMU1212m", + .driver = "Audigy2", .name = "E-mu 1010 [4001]", + .id = "EMU1010", .emu10k2_chip = 1, .ca0102_chip = 1, - .emu1212m = 1} , + .spk71 = 1, + .emu1010 = 1} , /* Tested by James@superbug.co.uk 3rd July 2005 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", @@ -1297,8 +1593,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card, } else if (emu->card_capabilities->ca_cardbus_chip) { if ((err = snd_emu10k1_cardbus_init(emu)) < 0) goto error; - } else if (emu->card_capabilities->emu1212m) { - if ((err = snd_emu10k1_emu1212m_init(emu)) < 0) { + } else if (emu->card_capabilities->emu1010) { + if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { snd_emu10k1_free(emu); return err; } @@ -1446,8 +1742,8 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) snd_emu10k1_ecard_init(emu); else if (emu->card_capabilities->ca_cardbus_chip) snd_emu10k1_cardbus_init(emu); - else if (emu->card_capabilities->emu1212m) - snd_emu10k1_emu1212m_init(emu); + else if (emu->card_capabilities->emu1010) + snd_emu10k1_emu1010_init(emu); else snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); snd_emu10k1_init(emu, emu->enable_ir, 1); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 2199b42a6019..bb0fec7f7e1b 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -460,7 +460,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream) u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); int i; - for(i=0; i < runtime->periods; i++) { + for(i = 0; i < runtime->periods; i++) { *table_base++=runtime->dma_addr+(i*period_size_bytes); *table_base++=period_size_bytes<<16; } @@ -1042,8 +1042,8 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff) - && (channel_id >=0) && (channel_id <= 2) ) + if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff) + && (channel_id >= 0) && (channel_id <= 2) ) snd_emu10k1x_ptr_write(emu, reg, channel_id, val); } } diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 13cd6ce89811..d8e8db89535f 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -3,6 +3,9 @@ * Creative Labs, Inc. * Routines for effect processor FX8010 * + * Copyright (c) by James Courtier-Dutton + * Added EMU 1010 support. + * * BUGS: * -- * @@ -1069,6 +1072,21 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; } +static int snd_emu10k1_audigy_dsp_convert_32_to_2x16( + struct snd_emu10k1_fx8010_code *icode, + u32 *ptr, int tmp, int bit_shifter16, + int reg_in, int reg_out) +{ + A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000); + A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000); + A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2)); + A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000); + A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000); + A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000); + A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2)); + A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000); + return 1; +} /* * initial DSP configuration for Audigy @@ -1077,6 +1095,7 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) { int err, i, z, gpr, nctl; + int bit_shifter16; const int playback = 10; const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */ const int stereo_mix = capture + 2; @@ -1114,17 +1133,14 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ptr = 0; nctl = 0; gpr = stereo_mix + 10; + gpr_map[gpr++] = 0x00007fff; + gpr_map[gpr++] = 0x00008000; + gpr_map[gpr++] = 0x0000ffff; + bit_shifter16 = gpr; /* stop FX processor */ snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); -#if 0 - /* FIX: jcd test */ - for (z = 0; z < 80; z=z+2) { - A_OP(icode, &ptr, iACC3, A_EXTOUT(z), A_FXBUS(FXBUS_PCM_LEFT_FRONT), A_C_00000000, A_C_00000000); /* left */ - A_OP(icode, &ptr, iACC3, A_EXTOUT(z+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT), A_C_00000000, A_C_00000000); /* right */ - } -#endif /* jcd test */ #if 1 /* PCM front Playback Volume (independent from stereo mix) */ A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT)); @@ -1182,13 +1198,20 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0); gpr += 2; - + /* * inputs */ #define A_ADD_VOLUME_IN(var,vol,input) \ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) + /* emu1212 DSP 0 and DSP 1 Capture */ + if (emu->card_capabilities->emu1010) { + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0); + gpr += 2; + } /* AC'97 Playback Volume - used only for mic (renamed later) */ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L); A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R); @@ -1429,6 +1452,13 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* digital outputs */ /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */ + if (emu->card_capabilities->emu1010) { + /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */ + snd_printk("EMU outputs on\n"); + for (z = 0; z < 8; z++) { + A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000); + } + } /* IEC958 Optical Raw Playback Switch */ gpr_map[gpr++] = 0; @@ -1466,9 +1496,57 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); #endif - /* EFX capture - capture the 16 EXTINs */ - for (z = 0; z < 16; z++) { - A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z)); + if (emu->card_capabilities->emu1010) { + snd_printk("EMU inputs on\n"); + /* Capture 8 channels of S32_LE sound */ + + /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */ + /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */ + /* A_P16VIN(0) is delayed by one sample, + * so all other A_P16VIN channels will need to also be delayed + */ + /* Left ADC in. 1 of 2 */ + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) ); + /* Right ADC in 1 of 2 */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000); + /* For 96kHz mode */ + /* Left ADC in. 2 of 2 */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000); + /* Right ADC in 2 of 2 */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000); + +#if 0 + for (z = 4; z < 8; z++) { + A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000); + } + for (z = 0xc; z < 0x10; z++) { + A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000); + } +#endif + } else { + /* EFX capture - capture the 16 EXTINs */ + /* Capture 16 channels of S16_LE sound */ + for (z = 0; z < 16; z++) { + A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z)); + } } #endif /* JCD test */ @@ -2138,7 +2216,7 @@ void snd_emu10k1_free_efx(struct snd_emu10k1 *emu) snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP); } -#if 0 // FIXME: who use them? +#if 0 /* FIXME: who use them? */ int snd_emu10k1_fx8010_tone_control_activate(struct snd_emu10k1 *emu, int output) { if (output < 0 || output >= 6) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index c31f3d0877fa..c8176dc8142f 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -5,6 +5,9 @@ * Routines for control of EMU10K1 chips / mixer routines * Multichannel PCM support Copyright (c) Lee Revell * + * Copyright (c) by James Courtier-Dutton + * Added EMU 1010 support. + * * BUGS: * -- * @@ -68,6 +71,311 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, return 0; } +static char *emu1010_src_texts[] = { + "Silence", + "Dock Mic A", + "Dock Mic B", + "Dock ADC1 Left", + "Dock ADC1 Right", + "Dock ADC2 Left", + "Dock ADC2 Right", + "Dock ADC3 Left", + "Dock ADC3 Right", + "0202 ADC Left", + "0202 ADC Right", + "0202 SPDIF Left", + "0202 SPDIF Right", + "ADAT 0", + "ADAT 1", + "ADAT 2", + "ADAT 3", + "ADAT 4", + "ADAT 5", + "ADAT 6", + "ADAT 7", + "DSP 0", + "DSP 1", + "DSP 2", + "DSP 3", + "DSP 4", + "DSP 5", + "DSP 6", + "DSP 7", + "DSP 8", + "DSP 9", + "DSP 10", + "DSP 11", + "DSP 12", + "DSP 13", + "DSP 14", + "DSP 15", + "DSP 16", + "DSP 17", + "DSP 18", + "DSP 19", + "DSP 20", + "DSP 21", + "DSP 22", + "DSP 23", + "DSP 24", + "DSP 25", + "DSP 26", + "DSP 27", + "DSP 28", + "DSP 29", + "DSP 30", + "DSP 31", +}; + +static unsigned int emu1010_src_regs[] = { + EMU_SRC_SILENCE,/* 0 */ + EMU_SRC_DOCK_MIC_A1, /* 1 */ + EMU_SRC_DOCK_MIC_B1, /* 2 */ + EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */ + EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */ + EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */ + EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */ + EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */ + EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */ + EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */ + EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */ + EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */ + EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */ + EMU_SRC_HANA_ADAT, /* 13 */ + EMU_SRC_HANA_ADAT+1, /* 14 */ + EMU_SRC_HANA_ADAT+2, /* 15 */ + EMU_SRC_HANA_ADAT+3, /* 16 */ + EMU_SRC_HANA_ADAT+4, /* 17 */ + EMU_SRC_HANA_ADAT+5, /* 18 */ + EMU_SRC_HANA_ADAT+6, /* 19 */ + EMU_SRC_HANA_ADAT+7, /* 20 */ + EMU_SRC_ALICE_EMU32A, /* 21 */ + EMU_SRC_ALICE_EMU32A+1, /* 22 */ + EMU_SRC_ALICE_EMU32A+2, /* 23 */ + EMU_SRC_ALICE_EMU32A+3, /* 24 */ + EMU_SRC_ALICE_EMU32A+4, /* 25 */ + EMU_SRC_ALICE_EMU32A+5, /* 26 */ + EMU_SRC_ALICE_EMU32A+6, /* 27 */ + EMU_SRC_ALICE_EMU32A+7, /* 28 */ + EMU_SRC_ALICE_EMU32A+8, /* 29 */ + EMU_SRC_ALICE_EMU32A+9, /* 30 */ + EMU_SRC_ALICE_EMU32A+0xa, /* 31 */ + EMU_SRC_ALICE_EMU32A+0xb, /* 32 */ + EMU_SRC_ALICE_EMU32A+0xc, /* 33 */ + EMU_SRC_ALICE_EMU32A+0xd, /* 34 */ + EMU_SRC_ALICE_EMU32A+0xe, /* 35 */ + EMU_SRC_ALICE_EMU32A+0xf, /* 36 */ + EMU_SRC_ALICE_EMU32B, /* 37 */ + EMU_SRC_ALICE_EMU32B+1, /* 38 */ + EMU_SRC_ALICE_EMU32B+2, /* 39 */ + EMU_SRC_ALICE_EMU32B+3, /* 40 */ + EMU_SRC_ALICE_EMU32B+4, /* 41 */ + EMU_SRC_ALICE_EMU32B+5, /* 42 */ + EMU_SRC_ALICE_EMU32B+6, /* 43 */ + EMU_SRC_ALICE_EMU32B+7, /* 44 */ + EMU_SRC_ALICE_EMU32B+8, /* 45 */ + EMU_SRC_ALICE_EMU32B+9, /* 46 */ + EMU_SRC_ALICE_EMU32B+0xa, /* 47 */ + EMU_SRC_ALICE_EMU32B+0xb, /* 48 */ + EMU_SRC_ALICE_EMU32B+0xc, /* 49 */ + EMU_SRC_ALICE_EMU32B+0xd, /* 50 */ + EMU_SRC_ALICE_EMU32B+0xe, /* 51 */ + EMU_SRC_ALICE_EMU32B+0xf, /* 52 */ +}; + +static unsigned int emu1010_output_dst[] = { + EMU_DST_DOCK_DAC1_LEFT1, /* 0 */ + EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */ + EMU_DST_DOCK_DAC2_LEFT1, /* 2 */ + EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */ + EMU_DST_DOCK_DAC3_LEFT1, /* 4 */ + EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */ + EMU_DST_DOCK_DAC4_LEFT1, /* 6 */ + EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */ + EMU_DST_DOCK_PHONES_LEFT1, /* 8 */ + EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */ + EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */ + EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */ + EMU_DST_HANA_SPDIF_LEFT1, /* 12 */ + EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */ + EMU_DST_HAMOA_DAC_LEFT1, /* 14 */ + EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */ + EMU_DST_HANA_ADAT, /* 16 */ + EMU_DST_HANA_ADAT+1, /* 17 */ + EMU_DST_HANA_ADAT+2, /* 18 */ + EMU_DST_HANA_ADAT+3, /* 19 */ + EMU_DST_HANA_ADAT+4, /* 20 */ + EMU_DST_HANA_ADAT+5, /* 21 */ + EMU_DST_HANA_ADAT+6, /* 22 */ + EMU_DST_HANA_ADAT+7, /* 23 */ +}; + +static unsigned int emu1010_input_dst[] = { + EMU_DST_ALICE2_EMU32_0, + EMU_DST_ALICE2_EMU32_1, + EMU_DST_ALICE2_EMU32_2, + EMU_DST_ALICE2_EMU32_3, + EMU_DST_ALICE2_EMU32_4, + EMU_DST_ALICE2_EMU32_5, + EMU_DST_ALICE2_EMU32_6, + EMU_DST_ALICE2_EMU32_7, + EMU_DST_ALICE2_EMU32_8, + EMU_DST_ALICE2_EMU32_9, + EMU_DST_ALICE2_EMU32_A, + EMU_DST_ALICE2_EMU32_B, + EMU_DST_ALICE2_EMU32_C, + EMU_DST_ALICE2_EMU32_D, + EMU_DST_ALICE2_EMU32_E, + EMU_DST_ALICE2_EMU32_F, + EMU_DST_ALICE_I2S0_LEFT, + EMU_DST_ALICE_I2S0_RIGHT, + EMU_DST_ALICE_I2S1_LEFT, + EMU_DST_ALICE_I2S1_RIGHT, + EMU_DST_ALICE_I2S2_LEFT, + EMU_DST_ALICE_I2S2_RIGHT, +}; + +static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 53; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + int channel; + + channel = (kcontrol->private_value) & 0xff; + ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel]; + return 0; +} + +static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + int change = 0; + unsigned int val; + int channel; + + channel = (kcontrol->private_value) & 0xff; + if (emu->emu1010.output_source[channel] != ucontrol->value.enumerated.item[0]) { + val = emu->emu1010.output_source[channel] = ucontrol->value.enumerated.item[0]; + change = 1; + snd_emu1010_fpga_link_dst_src_write(emu, + emu1010_output_dst[channel], emu1010_src_regs[val]); + } + return change; +} + +static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + int channel; + + channel = (kcontrol->private_value) & 0xff; + ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel]; + return 0; +} + +static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + int change = 0; + unsigned int val; + int channel; + + channel = (kcontrol->private_value) & 0xff; + if (emu->emu1010.input_source[channel] != ucontrol->value.enumerated.item[0]) { + val = emu->emu1010.input_source[channel] = ucontrol->value.enumerated.item[0]; + change = 1; + snd_emu1010_fpga_link_dst_src_write(emu, + emu1010_input_dst[channel], emu1010_src_regs[val]); + } + return change; +} + +#define EMU1010_SOURCE_OUTPUT(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_emu1010_input_output_source_info, \ + .get = snd_emu1010_output_source_get, \ + .put = snd_emu1010_output_source_put, \ + .private_value = chid \ +} + +static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { + EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Left", 0), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Right", 1), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Left", 2), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Right", 3), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Left", 4), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Right", 5), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Left", 6), + EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Right", 7), + EMU1010_SOURCE_OUTPUT("Playback Dock Phones Left", 8), + EMU1010_SOURCE_OUTPUT("Playback Dock Phones Right", 9), + EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Left", 0xa), + EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Right", 0xb), + EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Left", 0xc), + EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Right", 0xd), + EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Left", 0xe), + EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Right", 0xf), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 0", 0x10), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 1", 0x11), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 2", 0x12), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 3", 0x13), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 4", 0x14), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 5", 0x15), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 6", 0x16), + EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 7", 0x17), +}; + +#define EMU1010_SOURCE_INPUT(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_emu1010_input_output_source_info, \ + .get = snd_emu1010_input_source_get, \ + .put = snd_emu1010_input_source_put, \ + .private_value = chid \ +} + +static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = { + EMU1010_SOURCE_INPUT("DSP 0 CAPTURE ENUM", 0), + EMU1010_SOURCE_INPUT("DSP 1 CAPTURE ENUM", 1), + EMU1010_SOURCE_INPUT("DSP 2 CAPTURE ENUM", 2), + EMU1010_SOURCE_INPUT("DSP 3 CAPTURE ENUM", 3), + EMU1010_SOURCE_INPUT("DSP 4 CAPTURE ENUM", 4), + EMU1010_SOURCE_INPUT("DSP 5 CAPTURE ENUM", 5), + EMU1010_SOURCE_INPUT("DSP 6 CAPTURE ENUM", 6), + EMU1010_SOURCE_INPUT("DSP 7 CAPTURE ENUM", 7), + EMU1010_SOURCE_INPUT("DSP 8 CAPTURE ENUM", 8), + EMU1010_SOURCE_INPUT("DSP 9 CAPTURE ENUM", 9), + EMU1010_SOURCE_INPUT("DSP A CAPTURE ENUM", 0xa), + EMU1010_SOURCE_INPUT("DSP B CAPTURE ENUM", 0xb), + EMU1010_SOURCE_INPUT("DSP C CAPTURE ENUM", 0xc), + EMU1010_SOURCE_INPUT("DSP D CAPTURE ENUM", 0xd), + EMU1010_SOURCE_INPUT("DSP E CAPTURE ENUM", 0xe), + EMU1010_SOURCE_INPUT("DSP F CAPTURE ENUM", 0xf), + EMU1010_SOURCE_INPUT("DSP 10 CAPTURE ENUM", 0x10), + EMU1010_SOURCE_INPUT("DSP 11 CAPTURE ENUM", 0x11), + EMU1010_SOURCE_INPUT("DSP 12 CAPTURE ENUM", 0x12), + EMU1010_SOURCE_INPUT("DSP 13 CAPTURE ENUM", 0x13), + EMU1010_SOURCE_INPUT("DSP 14 CAPTURE ENUM", 0x14), + EMU1010_SOURCE_INPUT("DSP 15 CAPTURE ENUM", 0x15), +}; + #if 0 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1021,7 +1329,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, return err; } - if ( emu->card_capabilities->emu1212m) { + if ( emu->card_capabilities->emu1010) { ; /* Disable the snd_audigy_spdif_shared_spdif */ } else if (emu->audigy) { if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL) @@ -1045,6 +1353,21 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if ((err = snd_p16v_mixer(emu))) return err; } + + if ( emu->card_capabilities->emu1010) { + int i; + + for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu)); + if (err < 0) + return err; + } + } return 0; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 717e92ec9e0a..44d098ac86d5 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -147,7 +147,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic 1, &epcm->extra); if (err < 0) { - // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); + /* printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); */ for (i = 0; i < voices; i++) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); epcm->voices[i] = NULL; @@ -339,7 +339,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, } } - // setup routing + /* setup routing */ if (emu->audigy) { snd_emu10k1_ptr_write(emu, A_FXRT1, voice, snd_emu10k1_compose_audigy_fxrt1(send_routing)); @@ -353,8 +353,8 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, } else snd_emu10k1_ptr_write(emu, FXRT, voice, snd_emu10k1_compose_send_routing(send_routing)); - // Stop CA - // Assumption that PT is already 0 so no harm overwriting + /* Stop CA */ + /* Assumption that PT is already 0 so no harm overwriting */ snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); @@ -367,14 +367,14 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) | emu10k1_select_interprom(pitch_target) | (w_16 ? 0 : CCCA_8BITSELECT)); - // Clear filter delay memory + /* Clear filter delay memory */ snd_emu10k1_ptr_write(emu, Z1, voice, 0); snd_emu10k1_ptr_write(emu, Z2, voice, 0); - // invalidate maps + /* invalidate maps */ silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); - // modulation envelope + /* modulation envelope */ snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); @@ -385,12 +385,12 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); - // volume envelope + /* volume envelope */ snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); - // filter envelope + /* filter envelope */ snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); - // pitch envelope + /* pitch envelope */ snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); spin_unlock_irqrestore(&emu->reg_lock, flags); @@ -468,7 +468,7 @@ static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream) snd_emu10k1_voice_free(epcm->emu, epcm->extra); epcm->extra = NULL; } - for (i=0; i < NUM_EFX_PLAYBACK; i++) { + for (i = 0; i < NUM_EFX_PLAYBACK; i++) { if (epcm->voices[i]) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); epcm->voices[i] = NULL; @@ -637,7 +637,7 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e stereo = (!extra && runtime->channels == 2); sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; ccis = emu10k1_ccis(stereo, sample == 0); - // set cs to 2 * number of cache registers beside the invalidated + /* set cs to 2 * number of cache registers beside the invalidated */ cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1; if (cs > 16) cs = 16; for (i = 0; i < cs; i++) { @@ -646,14 +646,14 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample); } } - // reset cache + /* reset cache */ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); if (stereo) { snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); } - // fill cache + /* fill cache */ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); if (stereo) { snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis); @@ -732,7 +732,7 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, struct snd_emu10k1_pcm_mixer *mix; int result = 0; - // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); + /* printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); */ spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -778,10 +778,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - // hmm this should cause full and half full interrupt to be raised? + /* hmm this should cause full and half full interrupt to be raised? */ outl(epcm->capture_ipr, emu->port + IPR); snd_emu10k1_intr_enable(emu, epcm->capture_inte); - // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); + /* printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); */ switch (epcm->type) { case CAPTURE_AC97ADC: snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); @@ -790,6 +790,7 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream, if (emu->audigy) { snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val); snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2); + snd_printdd("cr_val=0x%x, cr_val2=0x%x\n", epcm->capture_cr_val, epcm->capture_cr_val2); } else snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); break; @@ -851,7 +852,7 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream * ptr -= runtime->buffer_size; } #endif - // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); + /* printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); */ return ptr; } @@ -868,7 +869,7 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream, spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - // prepare voices + /* prepare voices */ for (i = 0; i < NUM_EFX_PLAYBACK; i++) { snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]); } @@ -917,7 +918,7 @@ static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *s if (!epcm->running) return 0; if (epcm->first_ptr) { - udelay(50); // hack, it takes awhile until capture is started + udelay(50); /* hack, it takes awhile until capture is started */ epcm->first_ptr = 0; } ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; @@ -972,6 +973,28 @@ static struct snd_pcm_hardware snd_emu10k1_capture = .fifo_size = 0, }; +static struct snd_pcm_hardware snd_emu10k1_capture_efx = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .rate_min = 44100, + .rate_max = 192000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 384, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + /* * */ @@ -1016,7 +1039,7 @@ static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream) struct snd_emu10k1_pcm_mixer *mix; int i; - for (i=0; i < NUM_EFX_PLAYBACK; i++) { + for (i = 0; i < NUM_EFX_PLAYBACK; i++) { mix = &emu->efx_pcm_mixer[i]; mix->epcm = NULL; snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0); @@ -1045,7 +1068,7 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream) runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_efx_playback; - for (i=0; i < NUM_EFX_PLAYBACK; i++) { + for (i = 0; i < NUM_EFX_PLAYBACK; i++) { mix = &emu->efx_pcm_mixer[i]; mix->send_routing[0][0] = i; memset(&mix->send_volume, 0, sizeof(mix->send_volume)); @@ -1199,15 +1222,59 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) epcm->capture_idx_reg = FXIDX; substream->runtime->private_data = epcm; substream->runtime->private_free = snd_emu10k1_pcm_free_substream; - runtime->hw = snd_emu10k1_capture; + runtime->hw = snd_emu10k1_capture_efx; runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); - runtime->hw.channels_min = runtime->hw.channels_max = 0; - for (idx = 0; idx < nefx; idx++) { - if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { - runtime->hw.channels_min++; - runtime->hw.channels_max++; + if (emu->card_capabilities->emu1010) { + /* TODO + * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE + * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 + * rate_min = 44100, + * rate_max = 192000, + * channels_min = 8, + * channels_max = 8, + * Need to add mixer control to fix sample rate + * + * There are 16 mono channels of 16bits each. + * 24bit Audio uses 2x channels over 16bit + * 96kHz uses 2x channels over 48kHz + * 192kHz uses 4x channels over 48kHz + * So, for 48kHz 24bit, one has 8 channels + * for 96kHz 24bit, one has 4 channels + * for 192kHz 24bit, one has 2 channels + */ +#if 1 + /* For 48kHz */ + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + runtime->hw.channels_min = runtime->hw.channels_max = 8; +#endif +#if 0 + /* For 96kHz */ + runtime->hw.rates = SNDRV_PCM_RATE_96000; + runtime->hw.rate_min = runtime->hw.rate_max = 96000; + runtime->hw.channels_min = runtime->hw.channels_max = 4; +#endif +#if 0 + /* For 192kHz */ + runtime->hw.rates = SNDRV_PCM_RATE_192000; + runtime->hw.rate_min = runtime->hw.rate_max = 192000; + runtime->hw.channels_min = runtime->hw.channels_max = 2; +#endif + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + /* efx_voices_mask[0] is expected to be zero + * efx_voices_mask[1] is expected to have 16bits set + */ + } else { + runtime->hw.channels_min = runtime->hw.channels_max = 0; + for (idx = 0; idx < nefx; idx++) { + if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { + runtime->hw.channels_min++; + runtime->hw.channels_max++; + } } } epcm->capture_cr_val = emu->efx_voices_mask[0]; @@ -1460,7 +1527,7 @@ static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, unsigned int count, unsigned int tram_shift) { - // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); + /* printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); */ if ((tram_shift & 1) == 0) { while (count--) { *dst_left-- = *src++; @@ -1537,7 +1604,7 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; unsigned int i; - // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); + /* printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); */ memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index b939e03aaedf..2c1585991bc8 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -3,6 +3,9 @@ * Creative Labs, Inc. * Routines for control of EMU10K1 chips / proc interface routines * + * Copyright (c) by James Courtier-Dutton + * Added EMU 1010 support. + * * BUGS: * -- * @@ -255,7 +258,7 @@ static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry, unsigned int val, tmp, n; val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0); tmp = (val >> 16) & 0x8; - for (n=0;n<4;n++) { + for (n = 0; n < 4; n++) { tmp = val >> (16 + (n*4)); if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]); else snd_iprintf(buffer, "Channel %d: No input\n", n); @@ -372,6 +375,27 @@ static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry, } #ifdef CONFIG_SND_DEBUG +static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_emu10k1 *emu = entry->private_data; + unsigned long value; + unsigned long flags; + unsigned long regs; + int i; + snd_iprintf(buffer, "EMU1010 Registers:\n\n"); + + for(i = 0; i < 0x30; i+=1) { + spin_lock_irqsave(&emu->emu_lock, flags); + regs=i+0x40; /* 0x40 upwards are registers. */ + outl(regs, emu->port + A_IOCFG); + outl(regs | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + value = inl(emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_iprintf(buffer, "%02X: %08lX, %02lX\n", i, value, (value >> 8) & 0x7f); + } +} + static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -398,7 +422,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) { + if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) { spin_lock_irqsave(&emu->emu_lock, flags); outl(val, emu->port + (reg & 0xfffffffc)); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -474,7 +498,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0xa0) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) ) + if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) ) snd_ptr_write(emu, iobase, reg, channel_id, val); } } @@ -531,6 +555,10 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu) { struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG + if ((emu->card_capabilities->emu1010) && + snd_card_proc_new(emu->card, "emu1010_regs", &entry)) { + snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); + } if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); entry->c.text.write = snd_emu_proc_io_reg_write; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 029e7856c43b..27ab7d1788a0 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -167,6 +167,51 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, return 0; } +int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value) +{ + if (reg < 0 || reg > 0x3f) + return 1; + reg += 0x40; /* 0x40 upwards are registers. */ + if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ + return 1; + outl(reg, emu->port + A_IOCFG); + udelay(10); + outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + udelay(10); + outl(value, emu->port + A_IOCFG); + udelay(10); + outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + + return 0; +} + +int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value) +{ + if (reg < 0 || reg > 0x3f) + return 1; + reg += 0x40; /* 0x40 upwards are registers. */ + outl(reg, emu->port + A_IOCFG); + udelay(10); + outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + udelay(10); + *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); + + return 0; +} + +/* Each Destination has one and only one Source, + * but one Source can feed any number of Destinations simultaneously. + */ +int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src) +{ + snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) ); + snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) ); + snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) ); + snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) ); + + return 0; +} + void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) { unsigned long flags; diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 4e0f95438f47..5da637c73393 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -253,7 +253,7 @@ static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream) struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); //struct snd_pcm_runtime *runtime = substream->runtime; //struct snd_emu10k1_pcm *epcm = runtime->private_data; - emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0; + emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0; /* FIXME: maybe zero others */ return 0; } @@ -264,7 +264,7 @@ static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream) struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); //struct snd_pcm_runtime *runtime = substream->runtime; //struct snd_emu10k1_pcm *epcm = runtime->private_data; - emu->p16v_capture_voice.use=0; + emu->p16v_capture_voice.use = 0; /* FIXME: maybe zero others */ return 0; } @@ -349,7 +349,7 @@ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream) break; } /* FIXME: Check emu->buffer.size before actually writing to it. */ - for(i=0; i < runtime->periods; i++) { + for(i = 0; i < runtime->periods; i++) { table_base[i*2]=runtime->dma_addr+(i*period_size_bytes); table_base[(i*2)+1]=period_size_bytes<<16; } @@ -394,7 +394,7 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream) /* FIXME: Check emu->buffer.size before actually writing to it. */ snd_emu10k1_ptr20_write(emu, 0x13, channel, 0); snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); - snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes + snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0); //snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */ //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<voices[(first_voice + i) % NUM_G]; // printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number); voice->use = 1; -- cgit v1.2.3-59-g8ed1b From 1700f3080d98323e91864d67cb9f6d46f818ccf0 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 4 Oct 2006 13:41:25 +0200 Subject: [ALSA] usb-audio: merge playback/capture hardware information structs The hardware information structures for playback and capture streams, respectively, are the same, so we can use just one structure for both streams. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 19bdcc74c96c..478e504f7028 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -1511,21 +1511,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_hardware snd_usb_playback = -{ - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 64, - .period_bytes_max = 512 * 1024, - .periods_min = 2, - .periods_max = 1024, -}; - -static struct snd_pcm_hardware snd_usb_capture = +static struct snd_pcm_hardware snd_usb_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -1904,8 +1890,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre return 0; } -static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction, - struct snd_pcm_hardware *hw) +static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) { struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; @@ -1913,7 +1898,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction, subs->interface = -1; subs->format = 0; - runtime->hw = *hw; + runtime->hw = snd_usb_hardware; runtime->private_data = subs; subs->pcm_substream = substream; return setup_hw_info(runtime, subs); @@ -1934,7 +1919,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) static int snd_usb_playback_open(struct snd_pcm_substream *substream) { - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback); + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK); } static int snd_usb_playback_close(struct snd_pcm_substream *substream) @@ -1944,7 +1929,7 @@ static int snd_usb_playback_close(struct snd_pcm_substream *substream) static int snd_usb_capture_open(struct snd_pcm_substream *substream) { - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture); + return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE); } static int snd_usb_capture_close(struct snd_pcm_substream *substream) -- cgit v1.2.3-59-g8ed1b From e4f8e656d8c152c08cd44d0e3c21f009fab09952 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 4 Oct 2006 13:42:57 +0200 Subject: [ALSA] usb-audio: allow pausing Add pause capabilities for both USB playback and capture streams. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 478e504f7028..d2e066dc5d81 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -391,6 +391,16 @@ static int retire_capture_urb(struct snd_usb_substream *subs, return 0; } +/* + * Process after capture complete when paused. Nothing to do. + */ +static int retire_paused_capture_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + return 0; +} + /* * prepare urb for full speed playback sync pipe @@ -493,13 +503,13 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) } /* - * Prepare urb for streaming before playback starts. + * Prepare urb for streaming before playback starts or when paused. * - * We don't yet have data, so we send a frame of silence. + * We don't have any data, so we send a frame of silence. */ -static int prepare_startup_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) +static int prepare_nodata_playback_urb(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) { unsigned int i, offs, counts; struct snd_urb_ctx *ctx = urb->context; @@ -622,7 +632,7 @@ static int retire_playback_urb(struct snd_usb_substream *subs, */ static struct snd_urb_ops audio_urb_ops[2] = { { - .prepare = prepare_startup_playback_urb, + .prepare = prepare_nodata_playback_urb, .retire = retire_playback_urb, .prepare_sync = prepare_playback_sync_urb, .retire_sync = retire_playback_sync_urb, @@ -637,7 +647,7 @@ static struct snd_urb_ops audio_urb_ops[2] = { static struct snd_urb_ops audio_urb_ops_high_speed[2] = { { - .prepare = prepare_startup_playback_urb, + .prepare = prepare_nodata_playback_urb, .retire = retire_playback_urb, .prepare_sync = prepare_playback_sync_urb_hs, .retire_sync = retire_playback_sync_urb_hs, @@ -925,10 +935,14 @@ static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: subs->ops.prepare = prepare_playback_urb; return 0; case SNDRV_PCM_TRIGGER_STOP: return deactivate_urbs(subs, 0, 0); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->ops.prepare = prepare_nodata_playback_urb; + return 0; default: return -EINVAL; } @@ -944,9 +958,16 @@ static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: + subs->ops.retire = retire_capture_urb; return start_urbs(subs, substream->runtime); case SNDRV_PCM_TRIGGER_STOP: return deactivate_urbs(subs, 0, 0); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->ops.retire = retire_paused_capture_urb; + return 0; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + subs->ops.retire = retire_capture_urb; + return 0; default: return -EINVAL; } @@ -1505,7 +1526,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { - subs->ops.prepare = prepare_startup_playback_urb; + subs->ops.prepare = prepare_nodata_playback_urb; return start_urbs(subs, runtime); } else return 0; @@ -1517,7 +1538,8 @@ static struct snd_pcm_hardware snd_usb_hardware = SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE, .buffer_bytes_max = 1024 * 1024, .period_bytes_min = 64, .period_bytes_max = 512 * 1024, -- cgit v1.2.3-59-g8ed1b From a58e7cb16dfae8a3c1c98a7ab7ca02a9e9b38921 Mon Sep 17 00:00:00 2001 From: Jochen Voss Date: Wed, 4 Oct 2006 18:04:10 +0200 Subject: [ALSA] Enable capture from line-in and CD on Revolution 5.1 Enable capture from line-in and CD on the Revolution 5.1 card. This patch adds support for switching between the 5 input channels of the AK5365 ADC and modifies the Revolution 5.1 driver to make use of this facility. Previously the capture channel was fixed to channel 0 (microphone on the Revolution 5.1 card). Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ak4xxx-adda.h | 2 + sound/i2c/other/ak4xxx-adda.c | 85 ++++++++++++++++++++++++++++++++++++++++++- sound/pci/ice1712/revo.c | 10 ++++- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index d0deca669b92..d01d53528015 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -50,6 +50,8 @@ struct snd_akm4xxx_adc_channel { char *name; /* capture gain volume label */ char *switch_name; /* capture switch */ unsigned int num_channels; + char *selector_name; /* capture source select label */ + const char **input_names; /* capture source names (NULL terminated) */ }; struct snd_akm4xxx { diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 5da49e2eb350..fe61b92f4e47 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -513,6 +513,66 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol, return change; } +#define AK5365_NUM_INPUTS 5 + +static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int mixer_ch = AK_GET_SHIFT(kcontrol->private_value); + const char **input_names; + int num_names, idx; + + input_names = ak->adc_info[mixer_ch].input_names; + + num_names = 0; + while (num_names < AK5365_NUM_INPUTS && input_names[num_names]) + ++num_names; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = num_names; + idx = uinfo->value.enumerated.item; + if (idx >= num_names) + return -EINVAL; + strncpy(uinfo->value.enumerated.name, input_names[idx], + sizeof(uinfo->value.enumerated.name)); + return 0; +} + +static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char val; + + val = snd_akm4xxx_get(ak, chip, addr) & mask; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char oval, val; + + oval = snd_akm4xxx_get(ak, chip, addr); + val = oval & ~mask; + val |= ucontrol->value.enumerated.item[0] & mask; + if (val != oval) { + snd_akm4xxx_write(ak, chip, addr, val); + return 1; + } + return 0; +} + /* * build AK4xxx controls */ @@ -647,9 +707,10 @@ static int build_adc_controls(struct snd_akm4xxx *ak) if (ak->type == SND_AK5365 && (idx % 2) == 0) { if (! ak->adc_info || - ! ak->adc_info[mixer_ch].switch_name) + ! ak->adc_info[mixer_ch].switch_name) { knew.name = "Capture Switch"; - else + knew.index = mixer_ch + ak->idx_offset * 2; + } else knew.name = ak->adc_info[mixer_ch].switch_name; knew.info = ak4xxx_switch_info; knew.get = ak4xxx_switch_get; @@ -662,6 +723,26 @@ static int build_adc_controls(struct snd_akm4xxx *ak) err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); if (err < 0) return err; + + memset(&knew, 0, sizeof(knew)); + knew.name = ak->adc_info[mixer_ch].selector_name; + if (!knew.name) { + knew.name = "Capture Channel"; + knew.index = mixer_ch + ak->idx_offset * 2; + } + + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.info = ak4xxx_capture_source_info; + knew.get = ak4xxx_capture_source_get; + knew.put = ak4xxx_capture_source_put; + knew.access = 0; + /* input selector control: reg. 1, bits 0-2. + * mis-use 'shift' to pass mixer_ch */ + knew.private_value + = AK_COMPOSE(idx/2, 1, mixer_ch, 0x07); + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); + if (err < 0) + return err; } idx += num_stereo; diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index bf98ea34feb0..d556de59b9ae 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -107,11 +107,19 @@ static struct snd_akm4xxx_dac_channel revo51_dac[] = { AK_DAC("PCM Rear Playback Volume", 2), }; +static const char *revo51_adc_input_names[] = { + "Mic", + "Line", + "CD", + NULL +}; + static struct snd_akm4xxx_adc_channel revo51_adc[] = { { .name = "PCM Capture Volume", .switch_name = "PCM Capture Switch", - .num_channels = 2 + .num_channels = 2, + .input_names = revo51_adc_input_names }, }; -- cgit v1.2.3-59-g8ed1b From feaa6a74d852be40c0e717471aa92eead012052c Mon Sep 17 00:00:00 2001 From: Jochen Voss Date: Wed, 4 Oct 2006 18:08:43 +0200 Subject: [ALSA] Enable the analog loopback of the Revolution 5.1 Enable the analog loopback of the Revolution 5.1 card. This patch adds support for the PT2258 volume controller and modifies the Revolution 5.1 driver to make use of this facility. This allows to control the analog loopback of the card. Signed-off-by: Jochen Voss Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/pt2258.h | 37 +++++++ sound/i2c/Makefile | 1 + sound/i2c/other/Makefile | 4 +- sound/i2c/other/pt2258.c | 233 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/ice1712/ice1712.h | 14 +++ sound/pci/ice1712/revo.c | 132 +++++++++++++++++++++++-- sound/pci/ice1712/revo.h | 6 +- 7 files changed, 413 insertions(+), 14 deletions(-) create mode 100644 include/sound/pt2258.h create mode 100644 sound/i2c/other/pt2258.c diff --git a/include/sound/pt2258.h b/include/sound/pt2258.h new file mode 100644 index 000000000000..160f812faa42 --- /dev/null +++ b/include/sound/pt2258.h @@ -0,0 +1,37 @@ +/* + * ALSA Driver for the PT2258 volume controller. + * + * Copyright (c) 2006 Jochen Voss + * + * 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 + * + */ + +#ifndef __SOUND_PT2258_H +#define __SOUND_PT2258_H + +struct snd_pt2258 { + struct snd_card *card; + struct snd_i2c_bus *i2c_bus; + struct snd_i2c_device *i2c_dev; + + unsigned char volume[6]; + int mute; +}; + +extern int snd_pt2258_reset(struct snd_pt2258 *pt); +extern int snd_pt2258_build_controls(struct snd_pt2258 *pt); + +#endif /* __SOUND_PT2258_H */ diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile index 816a2e7c88ca..45902d48c89c 100644 --- a/sound/i2c/Makefile +++ b/sound/i2c/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_SND) += other/ # Toplevel Module Dependency obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o +obj-$(CONFIG_SND_ICE1724) += snd-i2c.o diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile index 2fe023ef00a7..77a8a7c75dd9 100644 --- a/sound/i2c/other/Makefile +++ b/sound/i2c/other/Makefile @@ -6,11 +6,11 @@ snd-ak4114-objs := ak4114.o snd-ak4117-objs := ak4117.o snd-ak4xxx-adda-objs := ak4xxx-adda.o +snd-pt2258-objs := pt2258.o 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-ak4xxx-adda.o -obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o +obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c new file mode 100644 index 000000000000..50df1df2f2b9 --- /dev/null +++ b/sound/i2c/other/pt2258.c @@ -0,0 +1,233 @@ +/* + * ALSA Driver for the PT2258 volume controller. + * + * Copyright (c) 2006 Jochen Voss + * + * 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 + +MODULE_AUTHOR("Jochen Voss "); +MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)"); +MODULE_LICENSE("GPL"); + +#define PT2258_CMD_RESET 0xc0 +#define PT2258_CMD_UNMUTE 0xf8 +#define PT2258_CMD_MUTE 0xf9 + +static const unsigned char pt2258_channel_code[12] = { + 0x80, 0x90, /* channel 1: -10dB, -1dB */ + 0x40, 0x50, /* channel 2: -10dB, -1dB */ + 0x00, 0x10, /* channel 3: -10dB, -1dB */ + 0x20, 0x30, /* channel 4: -10dB, -1dB */ + 0x60, 0x70, /* channel 5: -10dB, -1dB */ + 0xa0, 0xb0 /* channel 6: -10dB, -1dB */ +}; + +int snd_pt2258_reset(struct snd_pt2258 *pt) +{ + unsigned char bytes[2]; + int i; + + /* reset chip */ + bytes[0] = PT2258_CMD_RESET; + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + /* mute all channels */ + pt->mute = 1; + bytes[0] = PT2258_CMD_MUTE; + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + /* set all channels to 0dB */ + for (i = 0; i < 6; ++i) + pt->volume[i] = 0; + bytes[0] = 0xd0; + bytes[1] = 0xe0; + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + return 0; + + __error: + snd_i2c_unlock(pt->i2c_bus); + snd_printk(KERN_ERR "PT2258 reset failed\n"); + return -EIO; +} + +static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 79; + return 0; +} + +static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pt2258 *pt = kcontrol->private_data; + int base = kcontrol->private_value; + + /* chip does not support register reads */ + ucontrol->value.integer.value[0] = 79 - pt->volume[base]; + ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1]; + return 0; +} + +static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pt2258 *pt = kcontrol->private_data; + int base = kcontrol->private_value; + unsigned char bytes[2]; + int val0, val1; + + val0 = 79 - ucontrol->value.integer.value[0]; + val1 = 79 - ucontrol->value.integer.value[1]; + if (val0 == pt->volume[base] && val1 == pt->volume[base + 1]) + return 0; + + pt->volume[base] = val0; + bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10); + bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10); + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + pt->volume[base + 1] = val1; + bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10); + bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10); + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + return 1; + + __error: + snd_i2c_unlock(pt->i2c_bus); + snd_printk(KERN_ERR "PT2258 access failed\n"); + return -EIO; +} + +static int pt2258_switch_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 pt2258_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pt2258 *pt = kcontrol->private_data; + + ucontrol->value.integer.value[0] = !pt->mute; + return 0; +} + +static int pt2258_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pt2258 *pt = kcontrol->private_data; + unsigned char bytes[2]; + int val; + + val = !ucontrol->value.integer.value[0]; + if (pt->mute == val) + return 0; + + pt->mute = val; + bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE; + snd_i2c_lock(pt->i2c_bus); + if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) + goto __error; + snd_i2c_unlock(pt->i2c_bus); + + return 1; + + __error: + snd_i2c_unlock(pt->i2c_bus); + snd_printk(KERN_ERR "PT2258 access failed 2\n"); + return -EIO; +} + +static DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0); + +int snd_pt2258_build_controls(struct snd_pt2258 *pt) +{ + struct snd_kcontrol_new knew; + char *names[3] = { + "Mic Loopback Playback Volume", + "Line Loopback Playback Volume", + "CD Loopback Playback Volume" + }; + int i, err; + + for (i = 0; i < 3; ++i) { + memset(&knew, 0, sizeof(knew)); + knew.name = names[i]; + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.count = 1; + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + knew.private_value = 2 * i; + knew.info = pt2258_stereo_volume_info; + knew.get = pt2258_stereo_volume_get; + knew.put = pt2258_stereo_volume_put; + knew.tlv.p = pt2258_db_scale; + + err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); + if (err < 0) + return err; + } + + memset(&knew, 0, sizeof(knew)); + knew.name = "Loopback Switch"; + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.info = pt2258_switch_info; + knew.get = pt2258_switch_get; + knew.put = pt2258_switch_put; + knew.access = 0; + err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); + if (err < 0) + return err; + + return 0; +} + +EXPORT_SYMBOL(snd_pt2258_reset); +EXPORT_SYMBOL(snd_pt2258_build_controls); diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index ce27eac40d4e..064542bf3af8 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -381,6 +382,11 @@ struct snd_ice1712 { unsigned short master[2]; unsigned short vol[8]; } phase28; + /* a non-standard I2C device for revo51 */ + struct revo51_spec { + struct snd_i2c_device *dev; + struct snd_pt2258 *pt2258; + } revo51; /* Hoontech-specific setting */ struct hoontech_spec { unsigned char boxbits[4]; @@ -462,6 +468,14 @@ static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice, snd_ice1712_gpio_write(ice, mask & bits); } +static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice, + unsigned int mask) +{ + ice->gpio.direction &= ~mask; + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + return (snd_ice1712_gpio_read(ice) & mask); +} + int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice); int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template, diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index d556de59b9ae..233e9a5a2e70 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -83,6 +83,102 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) snd_akm4xxx_reset(ak, 0); } +/* + * I2C access to the PT2258 volume controller on GPIO 6/7 (Revolution 5.1) + */ + +static void revo_i2c_start(struct snd_i2c_bus *bus) +{ + struct snd_ice1712 *ice = bus->private_data; + snd_ice1712_save_gpio_status(ice); +} + +static void revo_i2c_stop(struct snd_i2c_bus *bus) +{ + struct snd_ice1712 *ice = bus->private_data; + snd_ice1712_restore_gpio_status(ice); +} + +static void revo_i2c_direction(struct snd_i2c_bus *bus, int clock, int data) +{ + struct snd_ice1712 *ice = bus->private_data; + unsigned int mask, val; + + val = 0; + if (clock) + val |= VT1724_REVO_I2C_CLOCK; /* write SCL */ + if (data) + val |= VT1724_REVO_I2C_DATA; /* write SDA */ + mask = VT1724_REVO_I2C_CLOCK | VT1724_REVO_I2C_DATA; + ice->gpio.direction &= ~mask; + ice->gpio.direction |= val; + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + snd_ice1712_gpio_set_mask(ice, ~mask); +} + +static void revo_i2c_setlines(struct snd_i2c_bus *bus, int clk, int data) +{ + struct snd_ice1712 *ice = bus->private_data; + unsigned int val = 0; + + if (clk) + val |= VT1724_REVO_I2C_CLOCK; + if (data) + val |= VT1724_REVO_I2C_DATA; + snd_ice1712_gpio_write_bits(ice, + VT1724_REVO_I2C_DATA | + VT1724_REVO_I2C_CLOCK, val); + udelay(5); +} + +static int revo_i2c_getdata(struct snd_i2c_bus *bus, int ack) +{ + struct snd_ice1712 *ice = bus->private_data; + int bit; + + if (ack) + udelay(5); + bit = snd_ice1712_gpio_read_bits(ice, VT1724_REVO_I2C_DATA) ? 1 : 0; + return bit; +} + +static struct snd_i2c_bit_ops revo51_bit_ops = { + .start = revo_i2c_start, + .stop = revo_i2c_stop, + .direction = revo_i2c_direction, + .setlines = revo_i2c_setlines, + .getdata = revo_i2c_getdata, +}; + +static int revo51_i2c_init(struct snd_ice1712 *ice, + struct snd_pt2258 *pt) +{ + int err; + + /* create the I2C bus */ + err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c); + if (err < 0) + return err; + + ice->i2c->private_data = ice; + ice->i2c->hw_ops.bit = &revo51_bit_ops; + + /* create the I2C device */ + err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, + &ice->spec.revo51.dev); + if (err < 0) + return err; + + pt->card = ice->card; + pt->i2c_bus = ice->i2c; + pt->i2c_dev = ice->spec.revo51.dev; + ice->spec.revo51.pt2258 = pt; + + snd_pt2258_reset(pt); + + return 0; +} + /* * initialize the chips on M-Audio Revolution cards */ @@ -180,9 +276,9 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { .cif = 0, .data_mask = VT1724_REVO_CDOUT, .clk_mask = VT1724_REVO_CCLK, - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, - .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2, - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, + .cs_addr = VT1724_REVO_CS1, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, .add_flags = VT1724_REVO_CCLK, /* high at init */ .mask_flags = 0, }; @@ -198,13 +294,15 @@ static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = { .cif = 0, .data_mask = VT1724_REVO_CDOUT, .clk_mask = VT1724_REVO_CCLK, - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, - .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2, - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, + .cs_addr = VT1724_REVO_CS0, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, .add_flags = VT1724_REVO_CCLK, /* high at init */ .mask_flags = 0, }; +static struct snd_pt2258 ptc_revo51_volume; + static int __devinit revo_init(struct snd_ice1712 *ice) { struct snd_akm4xxx *ak; @@ -243,14 +341,20 @@ static int __devinit revo_init(struct snd_ice1712 *ice) break; case VT1724_SUBDEVICE_REVOLUTION51: ice->akm_codecs = 2; - if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0) + err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, + &akm_revo51_priv, ice); + if (err < 0) return err; - err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc, + err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo51_adc, &akm_revo51_adc_priv, ice); if (err < 0) return err; - /* unmute all codecs - needed! */ - snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); + err = revo51_i2c_init(ice, &ptc_revo51_volume); + if (err < 0) + return err; + /* unmute all codecs */ + snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, + VT1724_REVO_MUTE); break; } @@ -264,10 +368,18 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) switch (ice->eeprom.subvendor) { case VT1724_SUBDEVICE_REVOLUTION71: + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + break; case VT1724_SUBDEVICE_REVOLUTION51: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; + err = snd_pt2258_build_controls(ice->spec.revo51.pt2258); + if (err < 0) + return err; + break; } return 0; } diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h index efbb86ec3289..c70adaf017c1 100644 --- a/sound/pci/ice1712/revo.h +++ b/sound/pci/ice1712/revo.h @@ -42,9 +42,11 @@ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[]; #define VT1724_REVO_CCLK 0x02 #define VT1724_REVO_CDIN 0x04 /* not used */ #define VT1724_REVO_CDOUT 0x08 -#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */ +#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for (revo51) */ #define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */ -#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */ +#define VT1724_REVO_CS2 0x40 /* surround AKM4355 CS (revo71) */ +#define VT1724_REVO_I2C_DATA 0x40 /* I2C: PT 2258 SDA (on revo51) */ +#define VT1724_REVO_I2C_CLOCK 0x80 /* I2C: PT 2258 SCL (on revo51) */ #define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ #endif /* __SOUND_REVO_H */ -- cgit v1.2.3-59-g8ed1b From 12b131c4cf3eb1dc8a60082a434b7b100774c2e7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Oct 2006 15:05:34 +0200 Subject: [ALSA] allow registering an alsa device with struct device pointer This patch adds snd_register_device_for_dev taking a struct device pointer to link the new device to and makes snd_register_device a simple static inline wrapper around it. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/core.h | 37 ++++++++++++++++++++++++++++++++++--- sound/core/sound.c | 15 ++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/include/sound/core.h b/include/sound/core.h index 521f036cce99..83a575a29d0f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -211,9 +211,40 @@ extern struct class *sound_class; void snd_request_card(int card); -int snd_register_device(int type, struct snd_card *card, int dev, - const struct file_operations *f_ops, void *private_data, - const char *name); +int snd_register_device_for_dev(int type, struct snd_card *card, + int dev, + const struct file_operations *f_ops, + void *private_data, + const char *name, + struct device *device); + +/** + * snd_register_device - Register the ALSA device file for the card + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card: the card instance + * @dev: the device index + * @f_ops: the file operations + * @private_data: user pointer for f_ops->open() + * @name: the device file name + * + * Registers an ALSA device file for the given card. + * The operators have to be set in reg parameter. + * + * This function uses the card's device pointer to link to the + * correct &struct device. + * + * Returns zero if successful, or a negative error code on failure. + */ +static inline int snd_register_device(int type, struct snd_card *card, int dev, + const struct file_operations *f_ops, + void *private_data, + const char *name) +{ + return snd_register_device_for_dev(type, card, dev, f_ops, + private_data, name, + card ? card->dev : NULL); +} + int snd_unregister_device(int type, struct snd_card *card, int dev); void *snd_lookup_minor_data(unsigned int minor, int type); int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev, diff --git a/sound/core/sound.c b/sound/core/sound.c index 82a61c67cf3a..4084de064127 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -219,26 +219,27 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) #endif /** - * snd_register_device - Register the ALSA device file for the card + * snd_register_device_for_dev - Register the ALSA device file for the card * @type: the device type, SNDRV_DEVICE_TYPE_XXX * @card: the card instance * @dev: the device index * @f_ops: the file operations * @private_data: user pointer for f_ops->open() * @name: the device file name + * @device: the &struct device to link this new device to * * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * - * Retrurns zero if successful, or a negative error code on failure. + * Returns zero if successful, or a negative error code on failure. */ -int snd_register_device(int type, struct snd_card *card, int dev, - const struct file_operations *f_ops, void *private_data, - const char *name) +int snd_register_device_for_dev(int type, struct snd_card *card, int dev, + const struct file_operations *f_ops, + void *private_data, + const char *name, struct device *device) { int minor; struct snd_minor *preg; - struct device *device = snd_card_get_device_link(card); snd_assert(name, return -EINVAL); preg = kmalloc(sizeof *preg, GFP_KERNEL); @@ -272,7 +273,7 @@ int snd_register_device(int type, struct snd_card *card, int dev, return 0; } -EXPORT_SYMBOL(snd_register_device); +EXPORT_SYMBOL(snd_register_device_for_dev); /* find the matching minor record * return the index of snd_minor, or -1 if not found -- cgit v1.2.3-59-g8ed1b From c78085fcd2ce7cd036e1488472eb41a64d70949a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Oct 2006 15:06:34 +0200 Subject: [ALSA] alsa core: add struct device pointer to struct snd_pcm This patch adds a struct device pointer to struct snd_pcm in order to be able to give it a different device than the card. It defaults to the card's device, however, so it should behave identically for drivers not touching the field. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/pcm.h | 1 + sound/core/pcm.c | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2f645dfd7f70..016c41893b06 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -427,6 +427,7 @@ struct snd_pcm { wait_queue_head_t open_wait; void *private_data; void (*private_free) (struct snd_pcm *pcm); + struct device *dev; /* actual hw device this belongs to */ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) struct snd_pcm_oss oss; #endif diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 8e0189885516..4701f07ee0ae 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -944,6 +944,7 @@ static int snd_pcm_dev_register(struct snd_device *device) struct list_head *list; char str[16]; struct snd_pcm *pcm = device->device_data; + struct device *dev; snd_assert(pcm != NULL && device != NULL, return -ENXIO); mutex_lock(®ister_mutex); @@ -966,11 +967,18 @@ static int snd_pcm_dev_register(struct snd_device *device) devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } - if ((err = snd_register_device(devtype, pcm->card, - pcm->device, - &snd_pcm_f_ops[cidx], - pcm, str)) < 0) - { + /* device pointer to use, pcm->dev takes precedence if + * it is assigned, otherwise fall back to card's device + * if possible */ + dev = pcm->dev; + if (!dev) + dev = pcm->card ? pcm->card->dev : NULL; + /* register pcm */ + err = snd_register_device_for_dev(devtype, pcm->card, + pcm->device, + &snd_pcm_f_ops[cidx], + pcm, str, dev); + if (err < 0) { list_del(&pcm->list); mutex_unlock(®ister_mutex); return err; -- cgit v1.2.3-59-g8ed1b From 73e85fe8452b950b93cfb61377f749e9b15437fb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Oct 2006 15:07:23 +0200 Subject: [ALSA] aoa: set device pointer in pcms This patch makes a few whitespace cleanups and makes i2sbus assign the new struct device pointer in struct snd_pcm so that the proper device symlink shows up in sysfs. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/core/snd-aoa-alsa.c | 2 +- sound/aoa/soundbus/i2sbus/i2sbus-pcm.c | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c index b42fdea77ed0..8c5a19bd602a 100644 --- a/sound/aoa/core/snd-aoa-alsa.c +++ b/sound/aoa/core/snd-aoa-alsa.c @@ -59,7 +59,7 @@ void aoa_alsa_cleanup(void) } int aoa_snd_device_new(snd_device_type_t type, - void * device_data, struct snd_device_ops * ops) + void * device_data, struct snd_device_ops * ops) { struct snd_card *card = aoa_get_card(); int err; diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c index 5eff30b10201..051bb9b200ee 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c @@ -901,11 +901,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, } if (!dev->pcm) { - err = snd_pcm_new(card, - dev->pcmname, - dev->pcmid, - 0, - 0, + err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0, &dev->pcm); if (err) { printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); @@ -915,6 +911,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, module_put(THIS_MODULE); return err; } + dev->pcm->dev = &dev->ofdev.dev; } /* ALSA yet again sucks. -- cgit v1.2.3-59-g8ed1b From d595ee7e0162ae66faa8c4c7d8c2069b40d64fed Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Oct 2006 15:08:23 +0200 Subject: [ALSA] aoa: fix up i2sbus_attach_codec This patch changes i2sbus_attach_codec to implement a proper error handling strategy using labels to jump to the right part. Since it has an elaborate set-up sequence it also needs that tear-down, which I had hard-coded inbetween all the checks. This increases readability and should reduce .text size as well. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/soundbus/i2sbus/i2sbus-pcm.c | 72 ++++++++++++---------------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c index 051bb9b200ee..7c81db7e52c1 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c @@ -812,7 +812,6 @@ static void i2sbus_private_free(struct snd_pcm *pcm) module_put(THIS_MODULE); } -/* FIXME: this function needs an error handling strategy with labels */ int i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, struct codec_info *ci, void *data) @@ -880,24 +879,21 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, if (!cii->sdev) { printk(KERN_DEBUG "i2sbus: failed to get soundbus dev reference\n"); - kfree(cii); - return -ENODEV; + err = -ENODEV; + goto out_free_cii; } if (!try_module_get(THIS_MODULE)) { printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); - soundbus_dev_put(dev); - kfree(cii); - return -EBUSY; + err = -EBUSY; + goto out_put_sdev; } if (!try_module_get(ci->owner)) { printk(KERN_DEBUG "i2sbus: failed to get module reference to codec owner!\n"); - module_put(THIS_MODULE); - soundbus_dev_put(dev); - kfree(cii); - return -EBUSY; + err = -EBUSY; + goto out_put_this_module; } if (!dev->pcm) { @@ -905,11 +901,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, &dev->pcm); if (err) { printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); - kfree(cii); - module_put(ci->owner); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + goto out_put_ci_module; } dev->pcm->dev = &dev->ofdev.dev; } @@ -923,20 +915,12 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, /* eh? */ printk(KERN_ERR "Can't attach same bus to different cards!\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return -EINVAL; - } - if ((err = - snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) { - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + err = -EINVAL; + goto out_put_ci_module; } + err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1); + if (err) + goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, &i2sbus_playback_ops); i2sdev->out.created = 1; @@ -946,20 +930,11 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, if (dev->pcm->card != card) { printk(KERN_ERR "Can't attach same bus to different cards!\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return -EINVAL; - } - if ((err = - snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) { - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + goto out_put_ci_module; } + err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); + if (err) + goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, &i2sbus_record_ops); i2sdev->in.created = 1; @@ -974,11 +949,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, err = snd_device_register(card, dev->pcm); if (err) { printk(KERN_ERR "i2sbus: error registering new pcm\n"); - module_put(ci->owner); - kfree(cii); - soundbus_dev_put(dev); - module_put(THIS_MODULE); - return err; + goto out_put_ci_module; } /* no errors any more, so let's add this to our list */ list_add(&cii->list, &dev->codec_list); @@ -993,6 +964,15 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, 64 * 1024, 64 * 1024); return 0; + out_put_ci_module: + module_put(ci->owner); + out_put_this_module: + module_put(THIS_MODULE); + out_put_sdev: + soundbus_dev_put(dev); + out_free_cii: + kfree(cii); + return err; } void i2sbus_detach_codec(struct soundbus_dev *dev, void *data) -- cgit v1.2.3-59-g8ed1b From 9244b2c3079faac79b3b961116bd548c45087e2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 5 Oct 2006 16:02:22 +0200 Subject: [ALSA] alsa core: convert to list_for_each_entry* This patch converts most uses of list_for_each to list_for_each_entry all across alsa. In some place apparently an item can be on a list with different pointers so of course that isn't compatible with list_for_each, I therefore didn't touch those places. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/control.c | 37 +++++--------------- sound/core/control_compat.c | 5 ++- sound/core/device.c | 24 ++++--------- sound/core/hwdep.c | 10 ++---- sound/core/memalloc.c | 10 ++---- sound/core/pcm.c | 32 +++++++----------- sound/core/rawmidi.c | 29 ++++++---------- sound/core/seq/seq_clientmgr.c | 14 +++----- sound/core/seq/seq_device.c | 25 ++++++-------- sound/core/seq/seq_ports.c | 49 ++++++++++----------------- sound/core/seq/seq_virmidi.c | 4 +-- sound/core/timer.c | 77 ++++++++++++++---------------------------- 12 files changed, 102 insertions(+), 214 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 0c7bcd62e5b2..67f09b8f85e4 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -108,7 +108,6 @@ static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl) static int snd_ctl_release(struct inode *inode, struct file *file) { unsigned long flags; - struct list_head *list; struct snd_card *card; struct snd_ctl_file *ctl; struct snd_kcontrol *control; @@ -122,12 +121,10 @@ static int snd_ctl_release(struct inode *inode, struct file *file) list_del(&ctl->list); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); down_write(&card->controls_rwsem); - list_for_each(list, &card->controls) { - control = snd_kcontrol(list); + list_for_each_entry(control, &card->controls, list) for (idx = 0; idx < control->count; idx++) if (control->vd[idx].owner == ctl) control->vd[idx].owner = NULL; - } up_write(&card->controls_rwsem); snd_ctl_empty_read_queue(ctl); kfree(ctl); @@ -140,7 +137,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id) { unsigned long flags; - struct list_head *flist; struct snd_ctl_file *ctl; struct snd_kctl_event *ev; @@ -149,14 +145,11 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) card->mixer_oss_change_count++; #endif - list_for_each(flist, &card->ctl_files) { - struct list_head *elist; - ctl = snd_ctl_file(flist); + list_for_each_entry(ctl, &card->ctl_files, list) { if (!ctl->subscribed) continue; spin_lock_irqsave(&ctl->read_lock, flags); - list_for_each(elist, &ctl->events) { - ev = snd_kctl_event(elist); + list_for_each_entry(ev, &ctl->events, list) { if (ev->id.numid == id->numid) { ev->mask |= mask; goto _found; @@ -277,11 +270,9 @@ EXPORT_SYMBOL(snd_ctl_free_one); static unsigned int snd_ctl_hole_check(struct snd_card *card, unsigned int count) { - struct list_head *list; struct snd_kcontrol *kctl; - list_for_each(list, &card->controls) { - kctl = snd_kcontrol(list); + list_for_each_entry(kctl, &card->controls, list) { if ((kctl->id.numid <= card->last_numid && kctl->id.numid + kctl->count > card->last_numid) || (kctl->id.numid <= card->last_numid + count - 1 && @@ -498,12 +489,10 @@ EXPORT_SYMBOL(snd_ctl_rename_id); */ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) { - struct list_head *list; struct snd_kcontrol *kctl; snd_assert(card != NULL && numid != 0, return NULL); - list_for_each(list, &card->controls) { - kctl = snd_kcontrol(list); + list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) return kctl; } @@ -527,14 +516,12 @@ EXPORT_SYMBOL(snd_ctl_find_numid); struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, struct snd_ctl_elem_id *id) { - struct list_head *list; struct snd_kcontrol *kctl; snd_assert(card != NULL && id != NULL, return NULL); if (id->numid != 0) return snd_ctl_find_numid(card, id->numid); - list_for_each(list, &card->controls) { - kctl = snd_kcontrol(list); + list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.iface != id->iface) continue; if (kctl->id.device != id->device) @@ -1182,7 +1169,6 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg { struct snd_ctl_file *ctl; struct snd_card *card; - struct list_head *list; struct snd_kctl_ioctl *p; void __user *argp = (void __user *)arg; int __user *ip = argp; @@ -1232,8 +1218,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg #endif } down_read(&snd_ioctl_rwsem); - list_for_each(list, &snd_control_ioctls) { - p = list_entry(list, struct snd_kctl_ioctl, list); + list_for_each_entry(p, &snd_control_ioctls, list) { err = p->fioctl(card, ctl, cmd, arg); if (err != -ENOIOCTLCMD) { up_read(&snd_ioctl_rwsem); @@ -1357,13 +1342,11 @@ EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists) { - struct list_head *list; struct snd_kctl_ioctl *p; snd_assert(fcn != NULL, return -EINVAL); down_write(&snd_ioctl_rwsem); - list_for_each(list, lists) { - p = list_entry(list, struct snd_kctl_ioctl, list); + list_for_each_entry(p, lists, list) { if (p->fioctl == fcn) { list_del(&p->list); up_write(&snd_ioctl_rwsem); @@ -1453,7 +1436,6 @@ static int snd_ctl_dev_register(struct snd_device *device) static int snd_ctl_dev_disconnect(struct snd_device *device) { struct snd_card *card = device->device_data; - struct list_head *flist; struct snd_ctl_file *ctl; int err, cardnum; @@ -1462,8 +1444,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); down_read(&card->controls_rwsem); - list_for_each(flist, &card->ctl_files) { - ctl = snd_ctl_file(flist); + list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index ab48962c48ce..9311ca397bbc 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -392,7 +392,7 @@ enum { static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_ctl_file *ctl; - struct list_head *list; + struct snd_kctl_ioctl *p; void __user *argp = compat_ptr(arg); int err; @@ -427,8 +427,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns } down_read(&snd_ioctl_rwsem); - list_for_each(list, &snd_control_compat_ioctls) { - struct snd_kctl_ioctl *p = list_entry(list, struct snd_kctl_ioctl, list); + list_for_each_entry(p, &snd_control_compat_ioctls, list) { if (p->fioctl) { err = p->fioctl(ctl->card, ctl, cmd, arg); if (err != -ENOIOCTLCMD) { diff --git a/sound/core/device.c b/sound/core/device.c index ccb25816ac9e..5858b02b0b1d 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -79,13 +79,11 @@ EXPORT_SYMBOL(snd_device_new); */ int snd_device_free(struct snd_card *card, void *device_data) { - struct list_head *list; struct snd_device *dev; snd_assert(card != NULL, return -ENXIO); snd_assert(device_data != NULL, return -ENXIO); - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (dev->device_data != device_data) continue; /* unlink */ @@ -124,13 +122,11 @@ EXPORT_SYMBOL(snd_device_free); */ int snd_device_disconnect(struct snd_card *card, void *device_data) { - struct list_head *list; struct snd_device *dev; snd_assert(card != NULL, return -ENXIO); snd_assert(device_data != NULL, return -ENXIO); - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (dev->device_data != device_data) continue; if (dev->state == SNDRV_DEV_REGISTERED && @@ -161,14 +157,12 @@ int snd_device_disconnect(struct snd_card *card, void *device_data) */ int snd_device_register(struct snd_card *card, void *device_data) { - struct list_head *list; struct snd_device *dev; int err; snd_assert(card != NULL, return -ENXIO); snd_assert(device_data != NULL, return -ENXIO); - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (dev->device_data != device_data) continue; if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { @@ -192,13 +186,11 @@ EXPORT_SYMBOL(snd_device_register); */ int snd_device_register_all(struct snd_card *card) { - struct list_head *list; struct snd_device *dev; int err; snd_assert(card != NULL, return -ENXIO); - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { if ((err = dev->ops->dev_register(dev)) < 0) return err; @@ -215,12 +207,10 @@ int snd_device_register_all(struct snd_card *card) int snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - struct list_head *list; int err = 0; snd_assert(card != NULL, return -ENXIO); - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (snd_device_disconnect(card, dev->device_data) < 0) err = -ENXIO; } @@ -234,7 +224,6 @@ int snd_device_disconnect_all(struct snd_card *card) int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) { struct snd_device *dev; - struct list_head *list; int err; unsigned int range_low, range_high; @@ -242,8 +231,7 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; __again: - list_for_each(list, &card->devices) { - dev = snd_device(list); + list_for_each_entry(dev, &card->devices, list) { if (dev->type >= range_low && dev->type <= range_high) { if ((err = snd_device_free(card, dev->device_data)) < 0) return err; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 46b47689362c..a6a6ad0ad3c8 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -47,14 +47,11 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device); static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device) { - struct list_head *p; struct snd_hwdep *hwdep; - list_for_each(p, &snd_hwdep_devices) { - hwdep = list_entry(p, struct snd_hwdep, list); + list_for_each_entry(hwdep, &snd_hwdep_devices, list) if (hwdep->card == card && hwdep->device == device) return hwdep; - } return NULL; } @@ -468,15 +465,12 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) static void snd_hwdep_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct list_head *p; struct snd_hwdep *hwdep; mutex_lock(®ister_mutex); - list_for_each(p, &snd_hwdep_devices) { - hwdep = list_entry(p, struct snd_hwdep, list); + list_for_each_entry(hwdep, &snd_hwdep_devices, list) snd_iprintf(buffer, "%02i-%02i: %s\n", hwdep->card->number, hwdep->device, hwdep->name); - } mutex_unlock(®ister_mutex); } diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index bc0bd0910a62..f057430db0d0 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -406,19 +406,17 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) */ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) { - struct list_head *p; struct snd_mem_list *mem; snd_assert(dmab, return 0); mutex_lock(&list_mutex); - list_for_each(p, &mem_list_head) { - mem = list_entry(p, struct snd_mem_list, list); + list_for_each_entry(mem, &mem_list_head, list) { if (mem->id == id && (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL || ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) { struct device *dev = dmab->dev.dev; - list_del(p); + list_del(&mem->list); *dmab = mem->buffer; if (dmab->dev.dev == NULL) dmab->dev.dev = dev; @@ -488,7 +486,6 @@ static int snd_mem_proc_read(char *page, char **start, off_t off, { int len = 0; long pages = snd_allocated_pages >> (PAGE_SHIFT-12); - struct list_head *p; struct snd_mem_list *mem; int devno; static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; @@ -498,8 +495,7 @@ static int snd_mem_proc_read(char *page, char **start, off_t off, "pages : %li bytes (%li pages per %likB)\n", pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); devno = 0; - list_for_each(p, &mem_list_head) { - mem = list_entry(p, struct snd_mem_list, list); + list_for_each_entry(mem, &mem_list_head, list) { devno++; len += snprintf(page + len, count - len, "buffer %d : ID %08x : type %s\n", diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 4701f07ee0ae..76fcc5234d83 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -45,11 +45,9 @@ static int snd_pcm_dev_disconnect(struct snd_device *device); static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) { - struct list_head *p; struct snd_pcm *pcm; - list_for_each(p, &snd_pcm_devices) { - pcm = list_entry(p, struct snd_pcm, list); + list_for_each_entry(pcm, &snd_pcm_devices, list) { if (pcm->card == card && pcm->device == device) return pcm; } @@ -782,7 +780,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct snd_pcm_runtime *runtime; struct snd_ctl_file *kctl; struct snd_card *card; - struct list_head *list; int prefer_subdevice = -1; size_t size; @@ -795,8 +792,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, card = pcm->card; down_read(&card->controls_rwsem); - list_for_each(list, &card->ctl_files) { - kctl = snd_ctl_file(list); + list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == current->pid) { prefer_subdevice = kctl->prefer_pcm_subdevice; if (prefer_subdevice != -1) @@ -941,7 +937,7 @@ static int snd_pcm_dev_register(struct snd_device *device) { int cidx, err; struct snd_pcm_substream *substream; - struct list_head *list; + struct snd_pcm_notify *notify; char str[16]; struct snd_pcm *pcm = device->device_data; struct device *dev; @@ -988,11 +984,10 @@ static int snd_pcm_dev_register(struct snd_device *device) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } - list_for_each(list, &snd_pcm_notify_list) { - struct snd_pcm_notify *notify; - notify = list_entry(list, struct snd_pcm_notify, list); + + list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); - } + mutex_unlock(®ister_mutex); return 0; } @@ -1035,7 +1030,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { - struct list_head *p; + struct snd_pcm *pcm; snd_assert(notify != NULL && notify->n_register != NULL && @@ -1044,13 +1039,12 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) mutex_lock(®ister_mutex); if (nfree) { list_del(¬ify->list); - list_for_each(p, &snd_pcm_devices) - notify->n_unregister(list_entry(p, - struct snd_pcm, list)); + list_for_each_entry(pcm, &snd_pcm_devices, list) + notify->n_unregister(pcm); } else { list_add_tail(¬ify->list, &snd_pcm_notify_list); - list_for_each(p, &snd_pcm_devices) - notify->n_register(list_entry(p, struct snd_pcm, list)); + list_for_each_entry(pcm, &snd_pcm_devices, list) + notify->n_register(pcm); } mutex_unlock(®ister_mutex); return 0; @@ -1066,12 +1060,10 @@ EXPORT_SYMBOL(snd_pcm_notify); static void snd_pcm_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct list_head *p; struct snd_pcm *pcm; mutex_lock(®ister_mutex); - list_for_each(p, &snd_pcm_devices) { - pcm = list_entry(p, struct snd_pcm, list); + list_for_each_entry(pcm, &snd_pcm_devices, list) { snd_iprintf(buffer, "%02i-%02i: %s : %s", pcm->card->number, pcm->device, pcm->id, pcm->name); if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 0f055bfcbdac..7e6ceec738d5 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -61,14 +61,11 @@ static DEFINE_MUTEX(register_mutex); static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device) { - struct list_head *p; struct snd_rawmidi *rawmidi; - list_for_each(p, &snd_rawmidi_devices) { - rawmidi = list_entry(p, struct snd_rawmidi, list); + list_for_each_entry(rawmidi, &snd_rawmidi_devices, list) if (rawmidi->card == card && rawmidi->device == device) return rawmidi; - } return NULL; } @@ -389,7 +386,6 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) struct snd_rawmidi *rmidi; struct snd_rawmidi_file *rawmidi_file; wait_queue_t wait; - struct list_head *list; struct snd_ctl_file *kctl; if (maj == snd_major) { @@ -426,8 +422,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) while (1) { subdevice = -1; down_read(&card->controls_rwsem); - list_for_each(list, &card->ctl_files) { - kctl = snd_ctl_file(list); + list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == current->pid) { subdevice = kctl->prefer_rawmidi_subdevice; if (subdevice != -1) @@ -575,7 +570,6 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info struct snd_rawmidi *rmidi; struct snd_rawmidi_str *pstr; struct snd_rawmidi_substream *substream; - struct list_head *list; mutex_lock(®ister_mutex); rmidi = snd_rawmidi_search(card, info->device); @@ -589,8 +583,7 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info return -ENOENT; if (info->subdevice >= pstr->substream_count) return -ENXIO; - list_for_each(list, &pstr->substreams) { - substream = list_entry(list, struct snd_rawmidi_substream, list); + list_for_each_entry(substream, &pstr->substreams, list) { if ((unsigned int)substream->number == info->subdevice) return snd_rawmidi_info(substream, info); } @@ -1313,14 +1306,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *substream; struct snd_rawmidi_runtime *runtime; - struct list_head *list; rmidi = entry->private_data; snd_iprintf(buffer, "%s\n\n", rmidi->name); mutex_lock(&rmidi->open_mutex); if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { - list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { - substream = list_entry(list, struct snd_rawmidi_substream, list); + list_for_each_entry(substream, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, + list) { snd_iprintf(buffer, "Output %d\n" " Tx bytes : %lu\n", @@ -1339,8 +1332,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, } } if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) { - list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { - substream = list_entry(list, struct snd_rawmidi_substream, list); + list_for_each_entry(substream, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams, + list) { snd_iprintf(buffer, "Input %d\n" " Rx bytes : %lu\n", @@ -1625,13 +1619,10 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, struct snd_rawmidi_ops *ops) { - struct list_head *list; struct snd_rawmidi_substream *substream; - list_for_each(list, &rmidi->streams[stream].substreams) { - substream = list_entry(list, struct snd_rawmidi_substream, list); + list_for_each_entry(substream, &rmidi->streams[stream].substreams, list) substream->ops = ops; - } } /* diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 532a660df51d..bb9dd9fa8e51 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -659,7 +659,6 @@ static int deliver_to_subscribers(struct snd_seq_client *client, int err = 0, num_ev = 0; struct snd_seq_event event_saved; struct snd_seq_client_port *src_port; - struct list_head *p; struct snd_seq_port_subs_info *grp; src_port = snd_seq_port_use_ptr(client, event->source.port); @@ -674,8 +673,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, read_lock(&grp->list_lock); else down_read(&grp->list_mutex); - list_for_each(p, &grp->list_head) { - subs = list_entry(p, struct snd_seq_subscribers, src_list); + list_for_each_entry(subs, &grp->list_head, src_list) { event->dest = subs->info.dest; if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) /* convert time according to flag with subscription */ @@ -709,15 +707,14 @@ static int port_broadcast_event(struct snd_seq_client *client, { int num_ev = 0, err = 0; struct snd_seq_client *dest_client; - struct list_head *p; + struct snd_seq_client_port *port; dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST); if (dest_client == NULL) return 0; /* no matching destination */ read_lock(&dest_client->ports_lock); - list_for_each(p, &dest_client->ports_list_head) { - struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list); + list_for_each_entry(port, &dest_client->ports_list_head, list) { event->dest.port = port->addr.port; /* pass NULL as source client to avoid error bounce */ err = snd_seq_deliver_single_event(NULL, event, @@ -2473,11 +2470,10 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, struct snd_seq_client *client) { - struct list_head *l; + struct snd_seq_client_port *p; mutex_lock(&client->ports_mutex); - list_for_each(l, &client->ports_list_head) { - struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list); + list_for_each_entry(p, &client->ports_list_head, list) { snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n", p->addr.port, p->name, FLAG_PERM_RD(p->capability), diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index b79d011813c0..37852cdace76 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -106,11 +106,10 @@ static void remove_drivers(void); static void snd_seq_device_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct list_head *head; + struct ops_list *ops; mutex_lock(&ops_mutex); - list_for_each(head, &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); + list_for_each_entry(ops, &opslist, list) { snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", ops->id, ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), @@ -143,7 +142,7 @@ void snd_seq_autoload_unlock(void) void snd_seq_device_load_drivers(void) { #ifdef CONFIG_KMOD - struct list_head *head; + struct ops_list *ops; /* Calling request_module during module_init() * may cause blocking. @@ -155,8 +154,7 @@ void snd_seq_device_load_drivers(void) return; mutex_lock(&ops_mutex); - list_for_each(head, &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); + list_for_each_entry(ops, &opslist, list) { if (! (ops->driver & DRIVER_LOADED) && ! (ops->driver & DRIVER_REQUESTED)) { ops->used++; @@ -314,8 +312,8 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device) int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize) { - struct list_head *head; struct ops_list *ops; + struct snd_seq_device *dev; if (id == NULL || entry == NULL || entry->init_device == NULL || entry->free_device == NULL) @@ -341,8 +339,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, ops->argsize = argsize; /* initialize existing devices if necessary */ - list_for_each(head, &ops->dev_list) { - struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list); + list_for_each_entry(dev, &ops->dev_list, list) { init_device(dev, ops); } mutex_unlock(&ops->reg_mutex); @@ -394,8 +391,8 @@ static struct ops_list * create_driver(char *id) */ int snd_seq_device_unregister_driver(char *id) { - struct list_head *head; struct ops_list *ops; + struct snd_seq_device *dev; ops = find_driver(id, 0); if (ops == NULL) @@ -411,8 +408,7 @@ int snd_seq_device_unregister_driver(char *id) /* close and release all devices associated with this driver */ mutex_lock(&ops->reg_mutex); ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ - list_for_each(head, &ops->dev_list) { - struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list); + list_for_each_entry(dev, &ops->dev_list, list) { free_device(dev, ops); } @@ -512,11 +508,10 @@ static int free_device(struct snd_seq_device *dev, struct ops_list *ops) */ static struct ops_list * find_driver(char *id, int create_if_empty) { - struct list_head *head; + struct ops_list *ops; mutex_lock(&ops_mutex); - list_for_each(head, &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); + list_for_each_entry(ops, &opslist, list) { if (strcmp(ops->id, id) == 0) { ops->used++; mutex_unlock(&ops_mutex); diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 8c64b58ff77b..d88153438d69 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -59,14 +59,12 @@ much elements are in array. struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client, int num) { - struct list_head *p; struct snd_seq_client_port *port; if (client == NULL) return NULL; read_lock(&client->ports_lock); - list_for_each(p, &client->ports_list_head) { - port = list_entry(p, struct snd_seq_client_port, list); + list_for_each_entry(port, &client->ports_list_head, list) { if (port->addr.port == num) { if (port->closing) break; /* deleting now */ @@ -85,14 +83,12 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl struct snd_seq_port_info *pinfo) { int num; - struct list_head *p; struct snd_seq_client_port *port, *found; num = pinfo->addr.port; found = NULL; read_lock(&client->ports_lock); - list_for_each(p, &client->ports_list_head) { - port = list_entry(p, struct snd_seq_client_port, list); + list_for_each_entry(port, &client->ports_list_head, list) { if (port->addr.port < num) continue; if (port->addr.port == num) { @@ -131,8 +127,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int port) { unsigned long flags; - struct snd_seq_client_port *new_port; - struct list_head *l; + struct snd_seq_client_port *new_port, *p; int num = -1; /* sanity check */ @@ -161,15 +156,14 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, num = port >= 0 ? port : 0; mutex_lock(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); - list_for_each(l, &client->ports_list_head) { - struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list); + list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port > num) break; if (port < 0) /* auto-probe mode */ num = p->addr.port + 1; } /* insert the new port */ - list_add_tail(&new_port->list, l); + list_add_tail(&new_port->list, &p->list); client->num_ports++; new_port->addr.port = num; /* store the port number in the port */ write_unlock_irqrestore(&client->ports_lock, flags); @@ -287,16 +281,14 @@ static int port_delete(struct snd_seq_client *client, int snd_seq_delete_port(struct snd_seq_client *client, int port) { unsigned long flags; - struct list_head *l; - struct snd_seq_client_port *found = NULL; + struct snd_seq_client_port *found = NULL, *p; mutex_lock(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); - list_for_each(l, &client->ports_list_head) { - struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list); + list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) { /* ok found. delete from the list at first */ - list_del(l); + list_del(&p->list); client->num_ports--; found = p; break; @@ -314,7 +306,8 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) int snd_seq_delete_all_ports(struct snd_seq_client *client) { unsigned long flags; - struct list_head deleted_list, *p, *n; + struct list_head deleted_list; + struct snd_seq_client_port *port, *tmp; /* move the port list to deleted_list, and * clear the port list in the client data. @@ -331,9 +324,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client) write_unlock_irqrestore(&client->ports_lock, flags); /* remove each port in deleted_list */ - list_for_each_safe(p, n, &deleted_list) { - struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list); - list_del(p); + list_for_each_entry_safe(port, tmp, &deleted_list, list) { + list_del(&port->list); snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } @@ -500,8 +492,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector, { struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; - struct snd_seq_subscribers *subs; - struct list_head *p; + struct snd_seq_subscribers *subs, *s; int err, src_called = 0; unsigned long flags; int exclusive; @@ -525,13 +516,11 @@ int snd_seq_port_connect(struct snd_seq_client *connector, if (src->exclusive || dest->exclusive) goto __error; /* check whether already exists */ - list_for_each(p, &src->list_head) { - struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, src_list); + list_for_each_entry(s, &src->list_head, src_list) { if (match_subs_info(info, &s->info)) goto __error; } - list_for_each(p, &dest->list_head) { - struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, dest_list); + list_for_each_entry(s, &dest->list_head, dest_list) { if (match_subs_info(info, &s->info)) goto __error; } @@ -582,7 +571,6 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs; - struct list_head *p; int err = -ENOENT; unsigned long flags; @@ -590,8 +578,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); /* look for the connection */ - list_for_each(p, &src->list_head) { - subs = list_entry(p, struct snd_seq_subscribers, src_list); + list_for_each_entry(subs, &src->list_head, src_list) { if (match_subs_info(info, &subs->info)) { write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no lock yet @@ -620,12 +607,10 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp, struct snd_seq_addr *dest_addr) { - struct list_head *p; struct snd_seq_subscribers *s, *found = NULL; down_read(&src_grp->list_mutex); - list_for_each(p, &src_grp->list_head) { - s = list_entry(p, struct snd_seq_subscribers, src_list); + list_for_each_entry(s, &src_grp->list_head, src_list) { if (addr_match(dest_addr, &s->info.dest)) { found = s; break; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 0cfa06c6b81f..972f93405364 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -81,13 +81,11 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, struct snd_seq_event *ev) { struct snd_virmidi *vmidi; - struct list_head *list; unsigned char msg[4]; int len; read_lock(&rdev->filelist_lock); - list_for_each(list, &rdev->filelist) { - vmidi = list_entry(list, struct snd_virmidi, list); + list_for_each_entry(vmidi, &rdev->filelist, list) { if (!vmidi->trigger) continue; if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { diff --git a/sound/core/timer.c b/sound/core/timer.c index 10a79aed33f8..4e79f9ce1a85 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -130,11 +130,8 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner, static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) { struct snd_timer *timer = NULL; - struct list_head *p; - - list_for_each(p, &snd_timer_list) { - timer = list_entry(p, struct snd_timer, device_list); + list_for_each_entry(timer, &snd_timer_list, device_list) { if (timer->tmr_class != tid->dev_class) continue; if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || @@ -184,13 +181,10 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave) { struct snd_timer *timer; struct snd_timer_instance *master; - struct list_head *p, *q; /* FIXME: it's really dumb to look up all entries.. */ - list_for_each(p, &snd_timer_list) { - timer = list_entry(p, struct snd_timer, device_list); - list_for_each(q, &timer->open_list_head) { - master = list_entry(q, struct snd_timer_instance, open_list); + list_for_each_entry(timer, &snd_timer_list, device_list) { + list_for_each_entry(master, &timer->open_list_head, open_list) { if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { list_del(&slave->open_list); @@ -214,16 +208,13 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave) */ static void snd_timer_check_master(struct snd_timer_instance *master) { - struct snd_timer_instance *slave; - struct list_head *p, *n; + struct snd_timer_instance *slave, *tmp; /* check all pending slaves */ - list_for_each_safe(p, n, &snd_timer_slave_list) { - slave = list_entry(p, struct snd_timer_instance, open_list); + list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { - list_del(p); - list_add_tail(p, &master->slave_list_head); + list_move_tail(&slave->open_list, &master->slave_list_head); spin_lock_irq(&slave_active_lock); slave->master = master; slave->timer = master->timer; @@ -317,8 +308,7 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int snd_timer_close(struct snd_timer_instance *timeri) { struct snd_timer *timer = NULL; - struct list_head *p, *n; - struct snd_timer_instance *slave; + struct snd_timer_instance *slave, *tmp; snd_assert(timeri != NULL, return -ENXIO); @@ -353,12 +343,11 @@ int snd_timer_close(struct snd_timer_instance *timeri) timer->hw.close) timer->hw.close(timer); /* remove slave links */ - list_for_each_safe(p, n, &timeri->slave_list_head) { - slave = list_entry(p, struct snd_timer_instance, open_list); + list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, + open_list) { spin_lock_irq(&slave_active_lock); _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); - list_del(p); - list_add_tail(p, &snd_timer_slave_list); + list_move_tail(&slave->open_list, &snd_timer_slave_list); slave->master = NULL; slave->timer = NULL; spin_unlock_irq(&slave_active_lock); @@ -394,7 +383,6 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) unsigned long flags; unsigned long resolution = 0; struct snd_timer_instance *ts; - struct list_head *n; struct timespec tstamp; getnstimeofday(&tstamp); @@ -413,11 +401,9 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) return; spin_lock_irqsave(&timer->lock, flags); - list_for_each(n, &ti->slave_active_head) { - ts = list_entry(n, struct snd_timer_instance, active_list); + list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) ts->ccallback(ti, event + 100, &tstamp, resolution); - } spin_unlock_irqrestore(&timer->lock, flags); } @@ -593,10 +579,8 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l { struct snd_timer_instance *ti; unsigned long ticks = ~0UL; - struct list_head *p; - list_for_each(p, &timer->active_list_head) { - ti = list_entry(p, struct snd_timer_instance, active_list); + list_for_each_entry(ti, &timer->active_list_head, active_list) { if (ti->flags & SNDRV_TIMER_IFLG_START) { ti->flags &= ~SNDRV_TIMER_IFLG_START; ti->flags |= SNDRV_TIMER_IFLG_RUNNING; @@ -661,9 +645,9 @@ static void snd_timer_tasklet(unsigned long arg) */ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) { - struct snd_timer_instance *ti, *ts; + struct snd_timer_instance *ti, *ts, *tmp; unsigned long resolution, ticks; - struct list_head *p, *q, *n, *ack_list_head; + struct list_head *p, *ack_list_head; unsigned long flags; int use_tasklet = 0; @@ -679,12 +663,12 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) resolution = timer->hw.resolution; /* loop for all active instances - * Here we cannot use list_for_each because the active_list of a + * Here we cannot use list_for_each_entry because the active_list of a * processed instance is relinked to done_list_head before the callback * is called. */ - list_for_each_safe(p, n, &timer->active_list_head) { - ti = list_entry(p, struct snd_timer_instance, active_list); + list_for_each_entry_safe(ti, tmp, &timer->active_list_head, + active_list) { if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) continue; ti->pticks += ticks_left; @@ -700,7 +684,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) } else { ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; if (--timer->running) - list_del(p); + list_del(&ti->active_list); } if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) @@ -709,8 +693,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ack_list_head = &timer->sack_list_head; if (list_empty(&ti->ack_list)) list_add_tail(&ti->ack_list, ack_list_head); - list_for_each(q, &ti->slave_active_head) { - ts = list_entry(q, struct snd_timer_instance, active_list); + list_for_each_entry(ts, &ti->slave_active_head, active_list) { ts->pticks = ti->pticks; ts->resolution = resolution; if (list_empty(&ts->ack_list)) @@ -844,7 +827,6 @@ static int snd_timer_dev_register(struct snd_device *dev) { struct snd_timer *timer = dev->device_data; struct snd_timer *timer1; - struct list_head *p; snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); @@ -853,8 +835,7 @@ static int snd_timer_dev_register(struct snd_device *dev) return -EINVAL; mutex_lock(®ister_mutex); - list_for_each(p, &snd_timer_list) { - timer1 = list_entry(p, struct snd_timer, device_list); + list_for_each_entry(timer1, &snd_timer_list, device_list) { if (timer1->tmr_class > timer->tmr_class) break; if (timer1->tmr_class < timer->tmr_class) @@ -877,7 +858,7 @@ static int snd_timer_dev_register(struct snd_device *dev) mutex_unlock(®ister_mutex); return -EBUSY; } - list_add_tail(&timer->device_list, p); + list_add_tail(&timer->device_list, &timer1->device_list); mutex_unlock(®ister_mutex); return 0; } @@ -896,7 +877,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam unsigned long flags; unsigned long resolution = 0; struct snd_timer_instance *ti, *ts; - struct list_head *p, *n; if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) return; @@ -911,15 +891,12 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam else resolution = timer->hw.resolution; } - list_for_each(p, &timer->active_list_head) { - ti = list_entry(p, struct snd_timer_instance, active_list); + list_for_each_entry(ti, &timer->active_list_head, active_list) { if (ti->ccallback) ti->ccallback(ti, event, tstamp, resolution); - list_for_each(n, &ti->slave_active_head) { - ts = list_entry(n, struct snd_timer_instance, active_list); + list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) ts->ccallback(ts, event, tstamp, resolution); - } } spin_unlock_irqrestore(&timer->lock, flags); } @@ -1057,11 +1034,9 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, { struct snd_timer *timer; struct snd_timer_instance *ti; - struct list_head *p, *q; mutex_lock(®ister_mutex); - list_for_each(p, &snd_timer_list) { - timer = list_entry(p, struct snd_timer, device_list); + list_for_each_entry(timer, &snd_timer_list, device_list) { switch (timer->tmr_class) { case SNDRV_TIMER_CLASS_GLOBAL: snd_iprintf(buffer, "G%i: ", timer->tmr_device); @@ -1088,14 +1063,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) snd_iprintf(buffer, " SLAVE"); snd_iprintf(buffer, "\n"); - list_for_each(q, &timer->open_list_head) { - ti = list_entry(q, struct snd_timer_instance, open_list); + list_for_each_entry(ti, &timer->open_list_head, open_list) snd_iprintf(buffer, " Client %s : %s\n", ti->owner ? ti->owner : "unknown", ti->flags & (SNDRV_TIMER_IFLG_START | SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped"); - } } mutex_unlock(®ister_mutex); } -- cgit v1.2.3-59-g8ed1b From bbb53551e31dce3cdbf61330e135179a55c82fd1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 5 Oct 2006 16:21:19 +0200 Subject: [ALSA] emu10k1 - Fix compile warning Fixed a compile warning regarding print format for size_t. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/emu10k1/emu10k1_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 891172f2b1d7..09c4db8495b2 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -615,7 +615,7 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err); return err; } - snd_printk(KERN_INFO "firmware size=0x%x\n",fw_entry->size); + snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); if (fw_entry->size != 0x133a4) { snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From b66b3cfe6c2f6560f351278883a325b6ebc478f5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Oct 2006 09:34:20 +0200 Subject: [ALSA] hda_intel: increase maximum DMA buffer size to 1024MB See ALSA bug#2481 . Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1a7e82104bb9..d15c9b845f23 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1285,7 +1285,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - 1024 * 64, 1024 * 128); + 1024 * 64, 1024 * 1024); chip->pcm[pcm_dev] = pcm; if (chip->pcm_devs < pcm_dev + 1) chip->pcm_devs = pcm_dev + 1; -- cgit v1.2.3-59-g8ed1b From c7132aeb72ad1106dc76279de4d005f9e1c5815c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Oct 2006 15:12:29 +0200 Subject: [ALSA] pcm core: add prealloc_max file to substream directory to show maximum DMA size Users ask us many times about the maximum DMA size for PCM devices. This file gives them a hint in KB. Signed-off-by: Jaroslav Kysela --- include/sound/pcm.h | 1 + sound/core/pcm_memory.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 016c41893b06..ec006ed8cd59 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -384,6 +384,7 @@ struct snd_pcm_substream { struct snd_info_entry *proc_sw_params_entry; struct snd_info_entry *proc_status_entry; struct snd_info_entry *proc_prealloc_entry; + struct snd_info_entry *proc_prealloc_max_entry; #endif /* misc flags */ unsigned int hw_opened: 1; diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index be030cb4d373..95b1b2f0b1e2 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -101,6 +101,8 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) { snd_pcm_lib_preallocate_dma_free(substream); #ifdef CONFIG_SND_VERBOSE_PROCFS + snd_info_free_entry(substream->proc_prealloc_max_entry); + substream->proc_prealloc_max_entry = NULL; snd_info_free_entry(substream->proc_prealloc_entry); substream->proc_prealloc_entry = NULL; #endif @@ -141,6 +143,18 @@ static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024); } +/* + * read callback for prealloc_max proc file + * + * prints the maximum allowed size in kB. + */ +static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_pcm_substream *substream = entry->private_data; + snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_max / 1024); +} + /* * write callback for prealloc proc file * @@ -203,6 +217,15 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream) } } substream->proc_prealloc_entry = entry; + if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) { + entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_prealloc_max_entry = entry; } #else /* !CONFIG_SND_VERBOSE_PROCFS */ -- cgit v1.2.3-59-g8ed1b From 3388c37e04ec0e35ebc1b4c732fdefc9ea938f3b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 6 Oct 2006 17:06:39 +0200 Subject: [ALSA] intel8x0 - Use pci_iomap Use pci_iomap and ioread*/iowrite*() functions for accessing hardwares. pci_iomap is suitable for hardwares like ICH and compatible that have both PIO and MMIO. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/intel8x0.c | 120 ++++++++++++++++++-------------------------------- sound/pci/intel8x0m.c | 118 +++++++++++++++++-------------------------------- 2 files changed, 83 insertions(+), 155 deletions(-) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 30aaa6092a84..28d5d9deb892 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -368,12 +368,8 @@ struct intel8x0 { int irq; - unsigned int mmio; - unsigned long addr; - void __iomem *remap_addr; - unsigned int bm_mmio; - unsigned long bmaddr; - void __iomem *remap_bmaddr; + void __iomem *addr; + void __iomem *bmaddr; struct pci_dev *pci; struct snd_card *card; @@ -446,72 +442,48 @@ MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids); * Lowlevel I/O - busmaster */ -static u8 igetbyte(struct intel8x0 *chip, u32 offset) +static inline u8 igetbyte(struct intel8x0 *chip, u32 offset) { - if (chip->bm_mmio) - return readb(chip->remap_bmaddr + offset); - else - return inb(chip->bmaddr + offset); + return ioread8(chip->bmaddr + offset); } -static u16 igetword(struct intel8x0 *chip, u32 offset) +static inline u16 igetword(struct intel8x0 *chip, u32 offset) { - if (chip->bm_mmio) - return readw(chip->remap_bmaddr + offset); - else - return inw(chip->bmaddr + offset); + return ioread16(chip->bmaddr + offset); } -static u32 igetdword(struct intel8x0 *chip, u32 offset) +static inline u32 igetdword(struct intel8x0 *chip, u32 offset) { - if (chip->bm_mmio) - return readl(chip->remap_bmaddr + offset); - else - return inl(chip->bmaddr + offset); + return ioread32(chip->bmaddr + offset); } -static void iputbyte(struct intel8x0 *chip, u32 offset, u8 val) +static inline void iputbyte(struct intel8x0 *chip, u32 offset, u8 val) { - if (chip->bm_mmio) - writeb(val, chip->remap_bmaddr + offset); - else - outb(val, chip->bmaddr + offset); + iowrite8(val, chip->bmaddr + offset); } -static void iputword(struct intel8x0 *chip, u32 offset, u16 val) +static inline void iputword(struct intel8x0 *chip, u32 offset, u16 val) { - if (chip->bm_mmio) - writew(val, chip->remap_bmaddr + offset); - else - outw(val, chip->bmaddr + offset); + iowrite16(val, chip->bmaddr + offset); } -static void iputdword(struct intel8x0 *chip, u32 offset, u32 val) +static inline void iputdword(struct intel8x0 *chip, u32 offset, u32 val) { - if (chip->bm_mmio) - writel(val, chip->remap_bmaddr + offset); - else - outl(val, chip->bmaddr + offset); + iowrite32(val, chip->bmaddr + offset); } /* * Lowlevel I/O - AC'97 registers */ -static u16 iagetword(struct intel8x0 *chip, u32 offset) +static inline u16 iagetword(struct intel8x0 *chip, u32 offset) { - if (chip->mmio) - return readw(chip->remap_addr + offset); - else - return inw(chip->addr + offset); + return ioread16(chip->addr + offset); } -static void iaputword(struct intel8x0 *chip, u32 offset, u16 val) +static inline void iaputword(struct intel8x0 *chip, u32 offset, u16 val) { - if (chip->mmio) - writew(val, chip->remap_addr + offset); - else - outw(val, chip->addr + offset); + iowrite16(val, chip->addr + offset); } /* @@ -2443,10 +2415,10 @@ static int snd_intel8x0_free(struct intel8x0 *chip) fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0); snd_dma_free_pages(&chip->bdbars); } - if (chip->remap_addr) - iounmap(chip->remap_addr); - if (chip->remap_bmaddr) - iounmap(chip->remap_bmaddr); + if (chip->addr) + pci_iounmap(chip->pci, chip->addr); + if (chip->bmaddr) + pci_iounmap(chip->pci, chip->bmaddr); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); @@ -2793,35 +2765,27 @@ static int __devinit snd_intel8x0_create(struct snd_card *card, if (device_type == DEVICE_ALI) { /* ALI5455 has no ac97 region */ - chip->bmaddr = pci_resource_start(pci, 0); + chip->bmaddr = pci_iomap(pci, 0, 0); goto port_inited; } - if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */ - chip->mmio = 1; - chip->addr = pci_resource_start(pci, 2); - chip->remap_addr = ioremap_nocache(chip->addr, - pci_resource_len(pci, 2)); - if (chip->remap_addr == NULL) { - snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); - snd_intel8x0_free(chip); - return -EIO; - } - } else { - chip->addr = pci_resource_start(pci, 0); - } - if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */ - chip->bm_mmio = 1; - chip->bmaddr = pci_resource_start(pci, 3); - chip->remap_bmaddr = ioremap_nocache(chip->bmaddr, - pci_resource_len(pci, 3)); - if (chip->remap_bmaddr == NULL) { - snd_printk(KERN_ERR "Controller space ioremap problem\n"); - snd_intel8x0_free(chip); - return -EIO; - } - } else { - chip->bmaddr = pci_resource_start(pci, 1); + if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */ + chip->addr = pci_iomap(pci, 2, 0); + else + chip->addr = pci_iomap(pci, 0, 0); + if (!chip->addr) { + snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); + snd_intel8x0_free(chip); + return -EIO; + } + if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */ + chip->bmaddr = pci_iomap(pci, 3, 0); + else + chip->bmaddr = pci_iomap(pci, 1, 0); + if (!chip->bmaddr) { + snd_printk(KERN_ERR "Controller space ioremap problem\n"); + snd_intel8x0_free(chip); + return -EIO; } port_inited: @@ -3025,8 +2989,8 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci, snd_intel8x0_proc_init(chip); snprintf(card->longname, sizeof(card->longname), - "%s with %s at %#lx, irq %i", card->shortname, - snd_ac97_get_short_name(chip->ac97[0]), chip->addr, chip->irq); + "%s with %s at irq %i", card->shortname, + snd_ac97_get_short_name(chip->ac97[0]), chip->irq); if (! ac97_clock) intel8x0_measure_ac97_clock(chip); diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 09dcf923b547..936c3cf16936 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -196,12 +196,8 @@ struct intel8x0m { int irq; - unsigned int mmio; - unsigned long addr; - void __iomem *remap_addr; - unsigned int bm_mmio; - unsigned long bmaddr; - void __iomem *remap_bmaddr; + void __iomem *addr; + void __iomem *bmaddr; struct pci_dev *pci; struct snd_card *card; @@ -253,72 +249,48 @@ MODULE_DEVICE_TABLE(pci, snd_intel8x0m_ids); * Lowlevel I/O - busmaster */ -static u8 igetbyte(struct intel8x0m *chip, u32 offset) +static inline u8 igetbyte(struct intel8x0m *chip, u32 offset) { - if (chip->bm_mmio) - return readb(chip->remap_bmaddr + offset); - else - return inb(chip->bmaddr + offset); + return ioread8(chip->bmaddr + offset); } -static u16 igetword(struct intel8x0m *chip, u32 offset) +static inline u16 igetword(struct intel8x0m *chip, u32 offset) { - if (chip->bm_mmio) - return readw(chip->remap_bmaddr + offset); - else - return inw(chip->bmaddr + offset); + return ioread16(chip->bmaddr + offset); } -static u32 igetdword(struct intel8x0m *chip, u32 offset) +static inline u32 igetdword(struct intel8x0m *chip, u32 offset) { - if (chip->bm_mmio) - return readl(chip->remap_bmaddr + offset); - else - return inl(chip->bmaddr + offset); + return ioread32(chip->bmaddr + offset); } -static void iputbyte(struct intel8x0m *chip, u32 offset, u8 val) +static inline void iputbyte(struct intel8x0m *chip, u32 offset, u8 val) { - if (chip->bm_mmio) - writeb(val, chip->remap_bmaddr + offset); - else - outb(val, chip->bmaddr + offset); + iowrite8(val, chip->bmaddr + offset); } -static void iputword(struct intel8x0m *chip, u32 offset, u16 val) +static inline void iputword(struct intel8x0m *chip, u32 offset, u16 val) { - if (chip->bm_mmio) - writew(val, chip->remap_bmaddr + offset); - else - outw(val, chip->bmaddr + offset); + iowrite16(val, chip->bmaddr + offset); } -static void iputdword(struct intel8x0m *chip, u32 offset, u32 val) +static inline void iputdword(struct intel8x0m *chip, u32 offset, u32 val) { - if (chip->bm_mmio) - writel(val, chip->remap_bmaddr + offset); - else - outl(val, chip->bmaddr + offset); + iowrite32(val, chip->bmaddr + offset); } /* * Lowlevel I/O - AC'97 registers */ -static u16 iagetword(struct intel8x0m *chip, u32 offset) +static inline u16 iagetword(struct intel8x0m *chip, u32 offset) { - if (chip->mmio) - return readw(chip->remap_addr + offset); - else - return inw(chip->addr + offset); + return ioread16(chip->addr + offset); } -static void iaputword(struct intel8x0m *chip, u32 offset, u16 val) +static inline void iaputword(struct intel8x0m *chip, u32 offset, u16 val) { - if (chip->mmio) - writew(val, chip->remap_addr + offset); - else - outw(val, chip->addr + offset); + iowrite16(val, chip->addr + offset); } /* @@ -1019,10 +991,10 @@ static int snd_intel8x0_free(struct intel8x0m *chip) __hw_end: if (chip->bdbars.area) snd_dma_free_pages(&chip->bdbars); - if (chip->remap_addr) - iounmap(chip->remap_addr); - if (chip->remap_bmaddr) - iounmap(chip->remap_bmaddr); + if (chip->addr) + pci_iounmap(chip->pci, chip->addr); + if (chip->bmaddr) + pci_iounmap(chip->pci, chip->bmaddr); if (chip->irq >= 0) free_irq(chip->irq, chip); pci_release_regions(chip->pci); @@ -1173,35 +1145,27 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, if (device_type == DEVICE_ALI) { /* ALI5455 has no ac97 region */ - chip->bmaddr = pci_resource_start(pci, 0); + chip->bmaddr = pci_iomap(pci, 0, 0); goto port_inited; } - if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */ - chip->mmio = 1; - chip->addr = pci_resource_start(pci, 2); - chip->remap_addr = ioremap_nocache(chip->addr, - pci_resource_len(pci, 2)); - if (chip->remap_addr == NULL) { - snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); - snd_intel8x0_free(chip); - return -EIO; - } - } else { - chip->addr = pci_resource_start(pci, 0); + if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */ + chip->addr = pci_iomap(pci, 2, 0); + else + chip->addr = pci_iomap(pci, 0, 0); + if (!chip->addr) { + snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); + snd_intel8x0_free(chip); + return -EIO; } - if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */ - chip->bm_mmio = 1; - chip->bmaddr = pci_resource_start(pci, 3); - chip->remap_bmaddr = ioremap_nocache(chip->bmaddr, - pci_resource_len(pci, 3)); - if (chip->remap_bmaddr == NULL) { - snd_printk(KERN_ERR "Controller space ioremap problem\n"); - snd_intel8x0_free(chip); - return -EIO; - } - } else { - chip->bmaddr = pci_resource_start(pci, 1); + if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */ + chip->bmaddr = pci_iomap(pci, 3, 0); + else + chip->bmaddr = pci_iomap(pci, 1, 0); + if (!chip->bmaddr) { + snd_printk(KERN_ERR "Controller space ioremap problem\n"); + snd_intel8x0_free(chip); + return -EIO; } port_inited: @@ -1339,8 +1303,8 @@ static int __devinit snd_intel8x0m_probe(struct pci_dev *pci, snd_intel8x0m_proc_init(chip); - sprintf(card->longname, "%s at 0x%lx, irq %i", - card->shortname, chip->addr, chip->irq); + sprintf(card->longname, "%s at irq %i", + card->shortname, chip->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); -- cgit v1.2.3-59-g8ed1b From 808db4a4512bedd45b62de255f7eedb5d5b788b9 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:20:14 +0200 Subject: [ALSA] ASoC: core and dapm headers This patch adds the ASoC and DAPM headers. Features:- o Defines Digital Audio Interface (DAI) API o Defines Codec, Platform and Machine API o Defines Dynamic Audio Power Management API Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ac97_codec.h | 1 + include/sound/soc-dapm.h | 286 +++++++++++++++++++++++++++ include/sound/soc.h | 480 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 767 insertions(+) create mode 100644 include/sound/soc-dapm.h create mode 100644 include/sound/soc.h diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 33720397a904..5f7c78d9e379 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -425,6 +425,7 @@ struct snd_ac97_build_ops { struct snd_ac97_bus_ops { void (*reset) (struct snd_ac97 *ac97); + void (*warm_reset)(struct snd_ac97 *ac97); void (*write) (struct snd_ac97 *ac97, unsigned short reg, unsigned short val); unsigned short (*read) (struct snd_ac97 *ac97, unsigned short reg); void (*wait) (struct snd_ac97 *ac97); diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h new file mode 100644 index 000000000000..2b1ae8edc43c --- /dev/null +++ b/include/sound/soc-dapm.h @@ -0,0 +1,286 @@ +/* + * linux/sound/soc-dapm.h -- ALSA SoC Dynamic Audio Power Management + * + * Author: Liam Girdwood + * Created: Aug 11th 2005 + * Copyright: Wolfson Microelectronics. PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_SOC_DAPM_H +#define __LINUX_SND_SOC_DAPM_H + +#include +#include +#include +#include + +/* widget has no PM register bit */ +#define SND_SOC_NOPM -1 + +/* + * SoC dynamic audio power managment + * + * We can have upto 4 power domains + * 1. Codec domain - VREF, VMID + * Usually controlled at codec probe/remove, although can be set + * at stream time if power is not needed for sidetone, etc. + * 2. Platform/Machine domain - physically connected inputs and outputs + * Is platform/machine and user action specific, is set in the machine + * driver and by userspace e.g when HP are inserted + * 3. Path domain - Internal codec path mixers + * Are automatically set when mixer and mux settings are + * changed by the user. + * 4. Stream domain - DAC's and ADC's. + * Enabled when stream playback/capture is started. + */ + +/* codec domain */ +#define SND_SOC_DAPM_VMID(wname) \ +{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0} + +/* platform domain */ +#define SND_SOC_DAPM_INPUT(wname) \ +{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0} +#define SND_SOC_DAPM_OUTPUT(wname) \ +{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0} +#define SND_SOC_DAPM_MIC(wname, wevent) \ +{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} +#define SND_SOC_DAPM_HP(wname, wevent) \ +{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_SPK(wname, wevent) \ +{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_LINE(wname, wevent) \ +{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} + +/* path domain */ +#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ + wcontrols, wncontrols) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} +#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ + wcontrols, wncontrols)\ +{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} +#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ +{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} +#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ +{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} +#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} + +/* path domain with event - event handler must return 0 for success */ +#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ + wncontrols, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ + wncontrols, wevent, wflags) \ +{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ +{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ + wevent, wflags) \ +{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1 \ + .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ + wevent, wflags) \ +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ + .event = wevent, .event_flags = wflags} + +/* events that are pre and post DAPM */ +#define SND_SOC_DAPM_PRE(wname, wevent) \ +{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_POST(wname, wevent) \ +{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} + +/* stream domain */ +#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ + .shift = wshift, .invert = winvert} +#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \ +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ + .shift = wshift, .invert = winvert} + +/* dapm kcontrol types */ +#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } +#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \ + power) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\ + ((mask) << 16) | ((invert) << 24) } +#define SOC_DAPM_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = snd_soc_dapm_put_enum_double, \ + .private_value = (unsigned long)&xenum } + +/* dapm stream operations */ +#define SND_SOC_DAPM_STREAM_NOP 0x0 +#define SND_SOC_DAPM_STREAM_START 0x1 +#define SND_SOC_DAPM_STREAM_STOP 0x2 +#define SND_SOC_DAPM_STREAM_SUSPEND 0x4 +#define SND_SOC_DAPM_STREAM_RESUME 0x8 +#define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10 +#define SND_SOC_DAPM_STREAM_PAUSE_RELEASE 0x20 + +/* dapm event types */ +#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */ +#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */ +#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */ +#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ +#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ +#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ + +/* convenience event type detection */ +#define SND_SOC_DAPM_EVENT_ON(e) \ + (e & (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)) +#define SND_SOC_DAPM_EVENT_OFF(e) \ + (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)) + +struct snd_soc_dapm_widget; +enum snd_soc_dapm_type; +struct snd_soc_dapm_path; +struct snd_soc_dapm_pin; + +/* dapm controls */ +int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_new_control(struct snd_soc_codec *codec, + const struct snd_soc_dapm_widget *widget); + +/* dapm path setup */ +int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, + const char *sink_name, const char *control_name, const char *src_name); +int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec); +void snd_soc_dapm_free(struct snd_soc_device *socdev); + +/* dapm events */ +int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, + int event); + +/* dapm sys fs - used by the core */ +int snd_soc_dapm_sys_add(struct device *dev); + +/* dapm audio endpoint control */ +int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, + char *pin, int status); +int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec); + +/* dapm widget types */ +enum snd_soc_dapm_type { + snd_soc_dapm_input = 0, /* input pin */ + snd_soc_dapm_output, /* output pin */ + snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_mixer, /* mixes several analog signals together */ + snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ + snd_soc_dapm_adc, /* analog to digital converter */ + snd_soc_dapm_dac, /* digital to analog converter */ + snd_soc_dapm_micbias, /* microphone bias (power) */ + snd_soc_dapm_mic, /* microphone */ + snd_soc_dapm_hp, /* headphones */ + snd_soc_dapm_spk, /* speaker */ + snd_soc_dapm_line, /* line input/output */ + snd_soc_dapm_switch, /* analog switch */ + snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */ + snd_soc_dapm_pre, /* machine specific pre widget - exec first */ + snd_soc_dapm_post, /* machine specific post widget - exec last */ +}; + +/* dapm audio path between two widgets */ +struct snd_soc_dapm_path { + char *name; + char *long_name; + + /* source (input) and sink (output) widgets */ + struct snd_soc_dapm_widget *source; + struct snd_soc_dapm_widget *sink; + struct snd_kcontrol *kcontrol; + + /* status */ + u32 connect:1; /* source and sink widgets are connected */ + u32 walked:1; /* path has been walked */ + + struct list_head list_source; + struct list_head list_sink; + struct list_head list; +}; + +/* dapm widget */ +struct snd_soc_dapm_widget { + enum snd_soc_dapm_type id; + char *name; /* widget name */ + char *sname; /* stream name */ + struct snd_soc_codec *codec; + struct list_head list; + + /* dapm control */ + short reg; /* negative reg = no direct dapm */ + unsigned char shift; /* bits to shift */ + unsigned int saved_value; /* widget saved value */ + unsigned int value; /* widget current value */ + unsigned char power:1; /* block power status */ + unsigned char invert:1; /* invert the power bit */ + unsigned char active:1; /* active stream on DAC, ADC's */ + unsigned char connected:1; /* connected codec pin */ + unsigned char new:1; /* cnew complete */ + unsigned char ext:1; /* has external widgets */ + unsigned char muted:1; /* muted for pop reduction */ + unsigned char suspend:1; /* was active before suspend */ + unsigned char pmdown:1; /* waiting for timeout */ + + /* external events */ + unsigned short event_flags; /* flags to specify event types */ + int (*event)(struct snd_soc_dapm_widget*, int); + + /* kcontrols that relate to this widget */ + int num_kcontrols; + const struct snd_kcontrol_new *kcontrols; + + /* widget input and outputs */ + struct list_head sources; + struct list_head sinks; +}; + +#endif diff --git a/include/sound/soc.h b/include/sound/soc.h new file mode 100644 index 000000000000..ecdd1fac94b6 --- /dev/null +++ b/include/sound/soc.h @@ -0,0 +1,480 @@ +/* + * linux/sound/soc.h -- ALSA SoC Layer + * + * Author: Liam Girdwood + * Created: Aug 11th 2005 + * Copyright: Wolfson Microelectronics. PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_SOC_H +#define __LINUX_SND_SOC_H + +#include +#include +#include +#include +#include +#include +#include + +#define SND_SOC_VERSION "0.11.8" + +/* + * Convenience kcontrol builders + */ +#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\ + ((shift) << 12) | ((mask) << 16) | ((invert) << 24)) +#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\ + ((invert) << 31)) +#define SOC_SINGLE(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = snd_soc_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } +#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ + .put = snd_soc_put_volsw, \ + .private_value = (reg) | ((shift_left) << 8) | \ + ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) } +#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw_2r, \ + .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \ + .private_value = (reg_left) | ((shift) << 8) | \ + ((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) } +#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ +{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ + .mask = xmask, .texts = xtexts } +#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \ + SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts) +#define SOC_ENUM_SINGLE_EXT(xmask, xtexts) \ +{ .mask = xmask, .texts = xtexts } +#define SOC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ + .private_value = (unsigned long)&xenum } +#define SOC_SINGLE_EXT(xname, xreg, xmask, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_SINGLE_VALUE_EXT(xreg, xmask, xinvert) } +#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = xdata } +#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&xenum } + +/* + * Digital Audio Interface (DAI) types + */ +#define SND_SOC_DAI_AC97 0x1 +#define SND_SOC_DAI_I2S 0x2 +#define SND_SOC_DAI_PCM 0x4 + +/* + * DAI hardware audio formats + */ +#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */ +#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */ +#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */ +#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM or LRC */ +#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM or LRC */ +#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */ + +/* + * DAI hardware signal inversions + */ +#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */ +#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */ +#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */ + +/* + * DAI hardware clock masters + * This is wrt the codec, the inverse is true for the interface + * i.e. if the codec is clk and frm master then the interface is + * clk and frame slave. + */ +#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */ +#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */ +#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */ +#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */ + +#define SND_SOC_DAIFMT_FORMAT_MASK 0x00ff +#define SND_SOC_DAIFMT_INV_MASK 0x0f00 +#define SND_SOC_DAIFMT_CLOCK_MASK 0xf000 + +/* + * DAI hardware audio direction + */ +#define SND_SOC_DAIDIR_PLAYBACK 0x1 +#define SND_SOC_DAIDIR_CAPTURE 0x2 + +/* + * DAI hardware Time Division Multiplexing (TDM) Slots + * Left and Right data word positions + * This is measured in words (sample size) and not bits. + */ +#define SND_SOC_DAITDM_LRDW(l,r) ((l << 8) | r) + +/* + * DAI hardware clock ratios + * bit clock can either be a generated by dividing mclk or + * by multiplying sample rate, hence there are 2 definitions below + * depending on codec type. + */ +/* ratio of sample rate to mclk/sysclk */ +#define SND_SOC_FS_ALL 0xffff /* all mclk supported */ + +/* bit clock dividers */ +#define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */ +#define SND_SOC_FSBD_REAL(x) (ffs(x)) +#define SND_SOC_FSBD_ALL 0xffff /* all bit clock dividers supported */ + +/* bit clock ratio to sample rate */ +#define SND_SOC_FSB(x) (1 << ((x - 16) / 16)) +#define SND_SOC_FSB_REAL(x) (((ffs(x) - 1) * 16) + 16) +/* all bclk ratios supported */ +#define SND_SOC_FSB_ALL SND_SOC_FSBD_ALL + +/* + * DAI hardware flags + */ +/* use bfs mclk divider mode, else sample rate ratio */ +#define SND_SOC_DAI_BFS_DIV 0x1 + +/* + * AC97 codec ID's bitmask + */ +#define SND_SOC_DAI_AC97_ID0 (1 << 0) +#define SND_SOC_DAI_AC97_ID1 (1 << 1) +#define SND_SOC_DAI_AC97_ID2 (1 << 2) +#define SND_SOC_DAI_AC97_ID3 (1 << 3) + +struct snd_soc_device; +struct snd_soc_pcm_stream; +struct snd_soc_ops; +struct snd_soc_dai_mode; +struct snd_soc_pcm_runtime; +struct snd_soc_codec_dai; +struct snd_soc_cpu_dai; +struct snd_soc_codec; +struct snd_soc_machine_config; +struct soc_enum; +struct snd_soc_ac97_ops; +struct snd_soc_clock_info; + +typedef int (*hw_write_t)(void *,const char* ,int); +typedef int (*hw_read_t)(void *,char* ,int); + +extern struct snd_ac97_bus_ops soc_ac97_ops; + +/* pcm <-> DAI connect */ +void snd_soc_free_pcms(struct snd_soc_device *socdev); +int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); +int snd_soc_register_card(struct snd_soc_device *socdev); + +/* set runtime hw params */ +int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, + const struct snd_pcm_hardware *hw); +int snd_soc_get_rate(int rate); + +/* codec IO */ +#define snd_soc_read(codec, reg) codec->read(codec, reg) +#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value) + +/* codec register bit access */ +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, + unsigned short mask, unsigned short value); +int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, + unsigned short mask, unsigned short value); + +int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, + struct snd_ac97_bus_ops *ops, int num); +void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); + +/* + *Controls + */ +struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, + void *data, char *long_name); +int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +/* SoC PCM stream information */ +struct snd_soc_pcm_stream { + char *stream_name; + unsigned int rate_min; /* min rate */ + unsigned int rate_max; /* max rate */ + unsigned int channels_min; /* min channels */ + unsigned int channels_max; /* max channels */ + unsigned int active:1; /* stream is in use */ +}; + +/* SoC audio ops */ +struct snd_soc_ops { + int (*startup)(struct snd_pcm_substream *); + void (*shutdown)(struct snd_pcm_substream *); + int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); + int (*hw_free)(struct snd_pcm_substream *); + int (*prepare)(struct snd_pcm_substream *); + int (*trigger)(struct snd_pcm_substream *, int); +}; + +/* SoC DAI hardware mode */ +struct snd_soc_dai_mode { + u16 fmt; /* SND_SOC_DAIFMT_* */ + u16 tdm; /* SND_SOC_HWTDM_* */ + u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */ + u16 pcmrate; /* SND_SOC_HWRATE_* */ + u16 pcmdir:2; /* SND_SOC_HWDIR_* */ + u16 flags:8; /* hw flags */ + u16 fs; /* mclk to rate divider */ + u32 bfs; /* mclk to bclk dividers */ + unsigned long priv; /* private mode data */ +}; + +/* DAI capabilities */ +struct snd_soc_dai_cap { + int num_modes; /* number of DAI modes */ + struct snd_soc_dai_mode *mode; /* array of supported DAI modes */ +}; + +/* SoC Codec DAI */ +struct snd_soc_codec_dai { + char *name; + int id; + + /* DAI capabilities */ + struct snd_soc_pcm_stream playback; + struct snd_soc_pcm_stream capture; + struct snd_soc_dai_cap caps; + + /* DAI runtime info */ + struct snd_soc_dai_mode dai_runtime; + struct snd_soc_ops ops; + unsigned int (*config_sysclk)(struct snd_soc_codec_dai*, + struct snd_soc_clock_info *info, unsigned int clk); + int (*digital_mute)(struct snd_soc_codec *, + struct snd_soc_codec_dai*, int); + unsigned int mclk; /* the audio master clock */ + unsigned int pll_in; /* the PLL input clock */ + unsigned int pll_out; /* the PLL output clock */ + unsigned int clk_div; /* internal clock divider << 1 (for fractions) */ + unsigned int active; + unsigned char pop_wait:1; + + /* DAI private data */ + void *private_data; +}; + +/* SoC CPU DAI */ +struct snd_soc_cpu_dai { + + /* DAI description */ + char *name; + unsigned int id; + unsigned char type; + + /* DAI callbacks */ + int (*probe)(struct platform_device *pdev); + void (*remove)(struct platform_device *pdev); + int (*suspend)(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai); + int (*resume)(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai); + unsigned int (*config_sysclk)(struct snd_soc_cpu_dai *cpu_dai, + struct snd_soc_clock_info *info, unsigned int clk); + + /* DAI capabilities */ + struct snd_soc_pcm_stream capture; + struct snd_soc_pcm_stream playback; + struct snd_soc_dai_cap caps; + + /* DAI runtime info */ + struct snd_soc_dai_mode dai_runtime; + struct snd_soc_ops ops; + struct snd_pcm_runtime *runtime; + unsigned char active:1; + unsigned int mclk; + void *dma_data; + + /* DAI private data */ + void *private_data; +}; + +/* SoC Audio Codec */ +struct snd_soc_codec { + char *name; + struct module *owner; + struct mutex mutex; + + /* callbacks */ + int (*dapm_event)(struct snd_soc_codec *codec, int event); + + /* runtime */ + struct snd_card *card; + struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ + unsigned int active; + unsigned int pcm_devs; + void *private_data; + + /* codec IO */ + void *control_data; /* codec control (i2c/3wire) data */ + unsigned int (*read)(struct snd_soc_codec *, unsigned int); + int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); + hw_write_t hw_write; + hw_read_t hw_read; + void *reg_cache; + short reg_cache_size; + short reg_cache_step; + + /* dapm */ + struct list_head dapm_widgets; + struct list_head dapm_paths; + unsigned int dapm_state; + unsigned int suspend_dapm_state; + + /* codec DAI's */ + struct snd_soc_codec_dai *dai; + unsigned int num_dai; +}; + +/* codec device */ +struct snd_soc_codec_device { + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); + int (*suspend)(struct platform_device *pdev, pm_message_t state); + int (*resume)(struct platform_device *pdev); +}; + +/* SoC platform interface */ +struct snd_soc_platform { + char *name; + + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); + int (*suspend)(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai); + int (*resume)(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai); + + /* pcm creation and destruction */ + int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, + struct snd_pcm *); + void (*pcm_free)(struct snd_pcm *); + + /* platform stream ops */ + struct snd_pcm_ops *pcm_ops; +}; + +/* SoC machine DAI configuration, glues a codec and cpu DAI together */ +struct snd_soc_dai_link { + char *name; /* Codec name */ + char *stream_name; /* Stream name */ + + /* DAI */ + struct snd_soc_codec_dai *codec_dai; + struct snd_soc_cpu_dai *cpu_dai; + u32 flags; /* DAI config preference flags */ + + /* codec/machine specific init - e.g. add machine controls */ + int (*init)(struct snd_soc_codec *codec); + + /* audio sysclock configuration */ + unsigned int (*config_sysclk)(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info); +}; + +/* SoC machine */ +struct snd_soc_machine { + char *name; + + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); + + /* the pre and post PM functions are used to do any PM work before and + * after the codec and DAI's do any PM work. */ + int (*suspend_pre)(struct platform_device *pdev, pm_message_t state); + int (*suspend_post)(struct platform_device *pdev, pm_message_t state); + int (*resume_pre)(struct platform_device *pdev); + int (*resume_post)(struct platform_device *pdev); + + /* machine stream operations */ + struct snd_soc_ops *ops; + + /* CPU <--> Codec DAI links */ + struct snd_soc_dai_link *dai_link; + int num_links; +}; + +/* SoC Device - the audio subsystem */ +struct snd_soc_device { + struct device *dev; + struct snd_soc_machine *machine; + struct snd_soc_platform *platform; + struct snd_soc_codec *codec; + struct snd_soc_codec_device *codec_dev; + void *codec_data; +}; + +/* runtime channel data */ +struct snd_soc_pcm_runtime { + struct snd_soc_codec_dai *codec_dai; + struct snd_soc_cpu_dai *cpu_dai; + struct snd_soc_device *socdev; +}; + +/* enumerated kcontrol */ +struct soc_enum { + unsigned short reg; + unsigned short reg2; + unsigned char shift_l; + unsigned char shift_r; + unsigned int mask; + const char **texts; + void *dapm; +}; + +/* clocking configuration data */ +struct snd_soc_clock_info { + unsigned int rate; + unsigned int fs; + unsigned int bclk_master; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From db2a416556af0313db028147e4a22fef6f214f2f Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 6 Oct 2006 18:31:09 +0200 Subject: [ALSA] ASoC: core code This patch is the core of ASoC functionality. The ASoC core is designed to provide the following features :- o Codec independence. Allows reuse of codec drivers on other platforms and machines. o Platform driver code reuse. Reuse of platform specific audio DMA and DAI drivers on different machines. o Easy I2S/PCM digital audio interface configuration between codec and SoC. Each SoC interface and codec registers their audio interface capabilities with the core at initialisation. The capabilities are subsequently matched and configured at run time for best power and performance when the application hw params are known. o Machine specific controls/operations: Allow machines to add controls and operations to the audio subsystem. e.g. volume control for speaker amp. To achieve all this, ASoC splits an embedded audio system into 3 components :- 1. Codec driver: The codec driver is platform independent and contains audio controls, audio interface capabilities, codec dapm and codec IO functions. 2. Platform driver: The platform driver contains the audio dma engine and audio interface drivers (e.g. I2S, AC97, PCM) for that platform. 3. Machine driver: The machine driver handles any machine specific controls and audio events. i.e. turning on an amp at start of playback. Signed-off-by: Frank Mandarino Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 1920 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1920 insertions(+) create mode 100644 sound/soc/soc-core.c diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c new file mode 100644 index 000000000000..e841ad46c759 --- /dev/null +++ b/sound/soc/soc-core.c @@ -0,0 +1,1920 @@ +/* + * soc-core.c -- ALSA SoC Audio Layer + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * 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. + * + * Revision history + * 12th Aug 2005 Initial version. + * 25th Oct 2005 Working Codec, Interface and Platform registration. + * + * TODO: + * o Add hw rules to enforce rates, etc. + * o More testing with other codecs/machines. + * o Add more codecs and platforms to ensure good API coverage. + * o Support TDM on PCM and I2S + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug */ +#define SOC_DEBUG 0 +#if SOC_DEBUG +#define dbg(format, arg...) printk(format, ## arg) +#else +#define dbg(format, arg...) +#endif +/* debug DAI capabilities matching */ +#define SOC_DEBUG_DAI 0 +#if SOC_DEBUG_DAI +#define dbgc(format, arg...) printk(format, ## arg) +#else +#define dbgc(format, arg...) +#endif + +static DEFINE_MUTEX(pcm_mutex); +static DEFINE_MUTEX(io_mutex); +static struct workqueue_struct *soc_workq; +static struct work_struct soc_stream_work; +static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); + +/* supported sample rates */ +/* ATTENTION: these values depend on the definition in pcm.h! */ +static const unsigned int rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 +}; + +/* + * This is a timeout to do a DAPM powerdown after a stream is closed(). + * It can be used to eliminate pops between different playback streams, e.g. + * between two audio tracks. + */ +static int pmdown_time = 5000; +module_param(pmdown_time, int, 0); +MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); + +#ifdef CONFIG_SND_SOC_AC97_BUS +/* unregister ac97 codec */ +static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) +{ + if (codec->ac97->dev.bus) + device_unregister(&codec->ac97->dev); + return 0; +} + +/* stop no dev release warning */ +static void soc_ac97_device_release(struct device *dev){} + +/* register ac97 codec to bus */ +static int soc_ac97_dev_register(struct snd_soc_codec *codec) +{ + int err; + + codec->ac97->dev.bus = &ac97_bus_type; + codec->ac97->dev.parent = NULL; + codec->ac97->dev.release = soc_ac97_device_release; + + snprintf(codec->ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s", + codec->card->number, 0, codec->name); + err = device_register(&codec->ac97->dev); + if (err < 0) { + snd_printk(KERN_ERR "Can't register ac97 bus\n"); + codec->ac97->dev.bus = NULL; + return err; + } + return 0; +} +#endif + +static inline const char* get_dai_name(int type) +{ + switch(type) { + case SND_SOC_DAI_AC97: + return "AC97"; + case SND_SOC_DAI_I2S: + return "I2S"; + case SND_SOC_DAI_PCM: + return "PCM"; + } + return NULL; +} + +/* get rate format from rate */ +static inline int soc_get_rate_format(int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (rates[i] == rate) + return 1 << i; + } + return 0; +} + +/* gets the audio system mclk/sysclk for the given parameters */ +static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) +{ + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_machine *machine = socdev->machine; + int i; + + /* find the matching machine config and get it's mclk for the given + * sample rate and hardware format */ + for(i = 0; i < machine->num_links; i++) { + if (machine->dai_link[i].cpu_dai == rtd->cpu_dai && + machine->dai_link[i].config_sysclk) + return machine->dai_link[i].config_sysclk(rtd, info); + } + return 0; +} + +/* changes a bitclk multiplier mask to a divider mask */ +static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk, + unsigned int pcmfmt, unsigned int chn) +{ + int i, j; + u16 bfs_ = 0; + int size = snd_pcm_format_physical_width(pcmfmt), min = 0; + + if (size <= 0) + return 0; + + /* the minimum bit clock that has enough bandwidth */ + min = size * rate * chn; + dbgc("mult --> div min bclk %d with mclk %d\n", min, mclk); + + for (i = 0; i < 16; i++) { + if ((bfs >> i) & 0x1) { + j = rate * SND_SOC_FSB_REAL(1<= min) { + bfs_ |= SND_SOC_FSBD(mclk/j); + dbgc("mult --> div support mult %d\n", + SND_SOC_FSB_REAL(1<> i) & 0x1) { + j = mclk / (SND_SOC_FSBD_REAL(1<= min) { + bfs_ |= SND_SOC_FSB(j/rate); + dbgc("div --> mult support div %d\n", + SND_SOC_FSBD_REAL(1<private_data; + struct snd_soc_dai_mode *codec_dai_mode = NULL; + struct snd_soc_dai_mode *cpu_dai_mode = NULL; + struct snd_soc_clock_info clk_info; + unsigned int fs, mclk, codec_bfs, cpu_bfs, rate = params_rate(params), + chn, j, k, cpu_bclk, codec_bclk, pcmrate; + u16 fmt = 0; + + dbg("asoc: match version %s\n", SND_SOC_VERSION); + clk_info.rate = rate; + pcmrate = soc_get_rate_format(rate); + + /* try and find a match from the codec and cpu DAI capabilities */ + for (j = 0; j < rtd->codec_dai->caps.num_modes; j++) { + for (k = 0; k < rtd->cpu_dai->caps.num_modes; k++) { + codec_dai_mode = &rtd->codec_dai->caps.mode[j]; + cpu_dai_mode = &rtd->cpu_dai->caps.mode[k]; + + if (!(codec_dai_mode->pcmrate & cpu_dai_mode->pcmrate & + pcmrate)) { + dbgc("asoc: DAI[%d:%d] failed to match rate\n", j, k); + continue; + } + + fmt = codec_dai_mode->fmt & cpu_dai_mode->fmt; + if (!(fmt & SND_SOC_DAIFMT_FORMAT_MASK)) { + dbgc("asoc: DAI[%d:%d] failed to match format\n", j, k); + continue; + } + + if (!(fmt & SND_SOC_DAIFMT_CLOCK_MASK)) { + dbgc("asoc: DAI[%d:%d] failed to match clock masters\n", + j, k); + continue; + } + + if (!(fmt & SND_SOC_DAIFMT_INV_MASK)) { + dbgc("asoc: DAI[%d:%d] failed to match invert\n", j, k); + continue; + } + + if (!(codec_dai_mode->pcmfmt & cpu_dai_mode->pcmfmt)) { + dbgc("asoc: DAI[%d:%d] failed to match pcm format\n", j, k); + continue; + } + + if (!(codec_dai_mode->pcmdir & cpu_dai_mode->pcmdir)) { + dbgc("asoc: DAI[%d:%d] failed to match direction\n", j, k); + continue; + } + + /* todo - still need to add tdm selection */ + rtd->cpu_dai->dai_runtime.fmt = + rtd->codec_dai->dai_runtime.fmt = + 1 << (ffs(fmt & SND_SOC_DAIFMT_FORMAT_MASK) -1) | + 1 << (ffs(fmt & SND_SOC_DAIFMT_CLOCK_MASK) - 1) | + 1 << (ffs(fmt & SND_SOC_DAIFMT_INV_MASK) - 1); + clk_info.bclk_master = + rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK; + + /* make sure the ratio between rate and master + * clock is acceptable*/ + fs = (cpu_dai_mode->fs & codec_dai_mode->fs); + if (fs == 0) { + dbgc("asoc: DAI[%d:%d] failed to match FS\n", j, k); + continue; + } + clk_info.fs = rtd->cpu_dai->dai_runtime.fs = + rtd->codec_dai->dai_runtime.fs = fs; + + /* calculate audio system clocking using slowest clocks possible*/ + mclk = soc_get_mclk(rtd, &clk_info); + if (mclk == 0) { + dbgc("asoc: DAI[%d:%d] configuration not clockable\n", j, k); + dbgc("asoc: rate %d fs %d master %x\n", rate, fs, + clk_info.bclk_master); + continue; + } + + /* calculate word size (per channel) and frame size */ + rtd->codec_dai->dai_runtime.pcmfmt = + rtd->cpu_dai->dai_runtime.pcmfmt = + 1 << params_format(params); + + chn = params_channels(params); + /* i2s always has left and right */ + if (params_channels(params) == 1 && + rtd->cpu_dai->dai_runtime.fmt & (SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_LEFT_J)) + chn <<= 1; + + /* Calculate bfs - the ratio between bitclock and the sample rate + * We must take into consideration the dividers and multipliers + * used in the codec and cpu DAI modes. We always choose the + * lowest possible clocks to reduce power. + */ + if (codec_dai_mode->flags & cpu_dai_mode->flags & + SND_SOC_DAI_BFS_DIV) { + /* cpu & codec bfs dividers */ + rtd->cpu_dai->dai_runtime.bfs = + rtd->codec_dai->dai_runtime.bfs = + 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1); + } else if (codec_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { + /* normalise bfs codec divider & cpu mult */ + codec_bfs = soc_bfs_div_to_mult(codec_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + rtd->cpu_dai->dai_runtime.bfs = + 1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1); + cpu_bfs = soc_bfs_mult_to_div(cpu_dai_mode->bfs, rate, mclk, + rtd->codec_dai->dai_runtime.pcmfmt, chn); + rtd->codec_dai->dai_runtime.bfs = + 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1); + } else if (cpu_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { + /* normalise bfs codec mult & cpu divider */ + codec_bfs = soc_bfs_mult_to_div(codec_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + rtd->cpu_dai->dai_runtime.bfs = + 1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1); + cpu_bfs = soc_bfs_div_to_mult(cpu_dai_mode->bfs, rate, mclk, + rtd->codec_dai->dai_runtime.pcmfmt, chn); + rtd->codec_dai->dai_runtime.bfs = + 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1); + } else { + /* codec & cpu bfs rate multipliers */ + rtd->cpu_dai->dai_runtime.bfs = + rtd->codec_dai->dai_runtime.bfs = + 1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1); + } + + /* make sure the bit clock speed is acceptable */ + if (!rtd->cpu_dai->dai_runtime.bfs || + !rtd->codec_dai->dai_runtime.bfs) { + dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k); + dbgc("asoc: cpu_dai %x codec %x\n", + rtd->cpu_dai->dai_runtime.bfs, + rtd->codec_dai->dai_runtime.bfs); + dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt); + continue; + } + + goto found; + } + } + printk(KERN_ERR "asoc: no matching DAI found between codec and CPU\n"); + return -EINVAL; + +found: + /* we have matching DAI's, so complete the runtime info */ + rtd->codec_dai->dai_runtime.pcmrate = + rtd->cpu_dai->dai_runtime.pcmrate = + soc_get_rate_format(rate); + + rtd->codec_dai->dai_runtime.priv = codec_dai_mode->priv; + rtd->cpu_dai->dai_runtime.priv = cpu_dai_mode->priv; + rtd->codec_dai->dai_runtime.flags = codec_dai_mode->flags; + rtd->cpu_dai->dai_runtime.flags = cpu_dai_mode->flags; + + /* for debug atm */ + dbg("asoc: DAI[%d:%d] Match OK\n", j, k); + if (rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) { + codec_bclk = (rtd->codec_dai->dai_runtime.fs * params_rate(params)) / + SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs); + dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n", + rtd->codec_dai->dai_runtime.fs, mclk, + SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); + } else { + codec_bclk = params_rate(params) * + SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs); + dbg("asoc: codec fs %d mclk %d bfs mult %d bclk %d\n", + rtd->codec_dai->dai_runtime.fs, mclk, + SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); + } + if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) { + cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) / + SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs); + dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n", + rtd->cpu_dai->dai_runtime.fs, mclk, + SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); + } else { + cpu_bclk = params_rate(params) * + SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs); + dbg("asoc: cpu fs %d mclk %d bfs mult %d bclk %d\n", + rtd->cpu_dai->dai_runtime.fs, mclk, + SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); + } + + /* + * Check we have matching bitclocks. If we don't then it means the + * sysclock returned by either the codec or cpu DAI (selected by the + * machine sysclock function) is wrong compared with the supported DAI + * modes for the codec or cpu DAI. + */ + if (cpu_bclk != codec_bclk){ + printk(KERN_ERR + "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n" + ); + printk(KERN_ERR "asoc: codec %d != cpu %d\n", codec_bclk, cpu_bclk); + } + + switch(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + dbg("asoc: DAI codec BCLK master, LRC master\n"); + break; + case SND_SOC_DAIFMT_CBS_CFM: + dbg("asoc: DAI codec BCLK slave, LRC master\n"); + break; + case SND_SOC_DAIFMT_CBM_CFS: + dbg("asoc: DAI codec BCLK master, LRC slave\n"); + break; + case SND_SOC_DAIFMT_CBS_CFS: + dbg("asoc: DAI codec BCLK slave, LRC slave\n"); + break; + } + dbg("asoc: mode %x, invert %x\n", + rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK, + rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK); + dbg("asoc: audio rate %d chn %d fmt %x\n", params_rate(params), + params_channels(params), params_format(params)); + + return 0; +} + +static inline u32 get_rates(struct snd_soc_dai_mode *modes, int nmodes) +{ + int i; + u32 rates = 0; + + for(i = 0; i < nmodes; i++) + rates |= modes[i].pcmrate; + + return rates; +} + +static inline u64 get_formats(struct snd_soc_dai_mode *modes, int nmodes) +{ + int i; + u64 formats = 0; + + for(i = 0; i < nmodes; i++) + formats |= modes[i].pcmfmt; + + return formats; +} + +/* + * Called by ALSA when a PCM substream is opened, the runtime->hw record is + * then initialized and any private data can be allocated. This also calls + * startup for the cpu DAI, platform, machine and codec DAI. + */ +static int soc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec_dai *codec_dai = rtd->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + mutex_lock(&pcm_mutex); + + /* startup the audio subsystem */ + if (rtd->cpu_dai->ops.startup) { + ret = rtd->cpu_dai->ops.startup(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open interface %s\n", + rtd->cpu_dai->name); + goto out; + } + } + + if (platform->pcm_ops->open) { + ret = platform->pcm_ops->open(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); + goto platform_err; + } + } + + if (machine->ops && machine->ops->startup) { + ret = machine->ops->startup(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: %s startup failed\n", machine->name); + goto machine_err; + } + } + + if (rtd->codec_dai->ops.startup) { + ret = rtd->codec_dai->ops.startup(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open codec %s\n", + rtd->codec_dai->name); + goto codec_dai_err; + } + } + + /* create runtime params from DMA, codec and cpu DAI */ + if (runtime->hw.rates) + runtime->hw.rates &= + get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) & + get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes); + else + runtime->hw.rates = + get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) & + get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes); + if (runtime->hw.formats) + runtime->hw.formats &= + get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) & + get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes); + else + runtime->hw.formats = + get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) & + get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes); + + /* Check that the codec and cpu DAI's are compatible */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_min = + max(rtd->codec_dai->playback.rate_min, + rtd->cpu_dai->playback.rate_min); + runtime->hw.rate_max = + min(rtd->codec_dai->playback.rate_max, + rtd->cpu_dai->playback.rate_max); + runtime->hw.channels_min = + max(rtd->codec_dai->playback.channels_min, + rtd->cpu_dai->playback.channels_min); + runtime->hw.channels_max = + min(rtd->codec_dai->playback.channels_max, + rtd->cpu_dai->playback.channels_max); + } else { + runtime->hw.rate_min = + max(rtd->codec_dai->capture.rate_min, + rtd->cpu_dai->capture.rate_min); + runtime->hw.rate_max = + min(rtd->codec_dai->capture.rate_max, + rtd->cpu_dai->capture.rate_max); + runtime->hw.channels_min = + max(rtd->codec_dai->capture.channels_min, + rtd->cpu_dai->capture.channels_min); + runtime->hw.channels_max = + min(rtd->codec_dai->capture.channels_max, + rtd->cpu_dai->capture.channels_max); + } + + snd_pcm_limit_hw_rates(runtime); + if (!runtime->hw.rates) { + printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", + rtd->codec_dai->name, rtd->cpu_dai->name); + goto codec_dai_err; + } + if (!runtime->hw.formats) { + printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", + rtd->codec_dai->name, rtd->cpu_dai->name); + goto codec_dai_err; + } + if (!runtime->hw.channels_min || !runtime->hw.channels_max) { + printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", + rtd->codec_dai->name, rtd->cpu_dai->name); + goto codec_dai_err; + } + + dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name); + dbg("asoc: rate mask 0x%x \nasoc: min ch %d max ch %d\n + asoc: min rate %d max rate %d\n", + runtime->hw.rates, runtime->hw.channels_min, + runtime->hw.channels_max, runtime->hw.rate_min, runtime->hw.rate_max); + + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 1; + else + rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 1; + rtd->cpu_dai->active = rtd->codec_dai->active = 1; + rtd->cpu_dai->runtime = runtime; + socdev->codec->active++; + mutex_unlock(&pcm_mutex); + return 0; + +codec_dai_err: + if (machine->ops && machine->ops->shutdown) + machine->ops->shutdown(substream); + +machine_err: + if (platform->pcm_ops->close) + platform->pcm_ops->close(substream); + +platform_err: + if (rtd->cpu_dai->ops.shutdown) + rtd->cpu_dai->ops.shutdown(substream); +out: + mutex_unlock(&pcm_mutex); + return ret; +} + +/* + * Power down the audio subsytem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +static void close_delayed_work(void *data) +{ + struct snd_soc_device *socdev = data; + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec_dai *codec_dai; + int i; + + mutex_lock(&pcm_mutex); + for(i = 0; i < codec->num_dai; i++) { + codec_dai = &codec->dai[i]; + + dbg("pop wq checking: %s status: %s waiting: %s\n", + codec_dai->playback.stream_name, + codec_dai->playback.active ? "active" : "inactive", + codec_dai->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (codec_dai->pop_wait == 1) { + + codec_dai->pop_wait = 0; + snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); + + /* power down the codec power domain if no longer active */ + if (codec->active == 0) { + dbg("pop wq D3 %s %s\n", codec->name, + codec_dai->playback.stream_name); + if (codec->dapm_event) + codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); + } + } + } + mutex_unlock(&pcm_mutex); +} + +/* + * Called by ALSA when a PCM substream is closed. Private data can be + * freed here. The cpu DAI, codec DAI, machine and platform are also + * shutdown. + */ +static int soc_codec_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec *codec = socdev->codec; + + mutex_lock(&pcm_mutex); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 0; + else + rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 0; + + if (rtd->codec_dai->playback.active == 0 && + rtd->codec_dai->capture.active == 0) { + rtd->cpu_dai->active = rtd->codec_dai->active = 0; + } + codec->active--; + + if (rtd->cpu_dai->ops.shutdown) + rtd->cpu_dai->ops.shutdown(substream); + + if (rtd->codec_dai->ops.shutdown) + rtd->codec_dai->ops.shutdown(substream); + + if (machine->ops && machine->ops->shutdown) + machine->ops->shutdown(substream); + + if (platform->pcm_ops->close) + platform->pcm_ops->close(substream); + rtd->cpu_dai->runtime = NULL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* start delayed pop wq here for playback streams */ + rtd->codec_dai->pop_wait = 1; + queue_delayed_work(soc_workq, &soc_stream_work, + msecs_to_jiffies(pmdown_time)); + } else { + /* capture streams can be powered down now */ + snd_soc_dapm_stream_event(codec, rtd->codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); + + if (codec->active == 0 && rtd->codec_dai->pop_wait == 0){ + if (codec->dapm_event) + codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); + } + } + + mutex_unlock(&pcm_mutex); + return 0; +} + +/* + * Called by ALSA when the PCM substream is prepared, can set format, sample + * rate, etc. This function is non atomic and can be called multiple times, + * it can refer to the runtime info. + */ +static int soc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec *codec = socdev->codec; + int ret = 0; + + mutex_lock(&pcm_mutex); + if (platform->pcm_ops->prepare) { + ret = platform->pcm_ops->prepare(substream); + if (ret < 0) + goto out; + } + + if (rtd->codec_dai->ops.prepare) { + ret = rtd->codec_dai->ops.prepare(substream); + if (ret < 0) + goto out; + } + + if (rtd->cpu_dai->ops.prepare) + ret = rtd->cpu_dai->ops.prepare(substream); + + /* we only want to start a DAPM playback stream if we are not waiting + * on an existing one stopping */ + if (rtd->codec_dai->pop_wait) { + /* we are waiting for the delayed work to start */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_dapm_stream_event(codec, + rtd->codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + else { + rtd->codec_dai->pop_wait = 0; + cancel_delayed_work(&soc_stream_work); + if (rtd->codec_dai->digital_mute) + rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + } + } else { + /* no delayed work - do we need to power up codec */ + if (codec->dapm_state != SNDRV_CTL_POWER_D0) { + + if (codec->dapm_event) + codec->dapm_event(codec, SNDRV_CTL_POWER_D1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(codec, + rtd->codec_dai->playback.stream_name, + SND_SOC_DAPM_STREAM_START); + else + snd_soc_dapm_stream_event(codec, + rtd->codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + + if (codec->dapm_event) + codec->dapm_event(codec, SNDRV_CTL_POWER_D0); + if (rtd->codec_dai->digital_mute) + rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + + } else { + /* codec already powered - power on widgets */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(codec, + rtd->codec_dai->playback.stream_name, + SND_SOC_DAPM_STREAM_START); + else + snd_soc_dapm_stream_event(codec, + rtd->codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + if (rtd->codec_dai->digital_mute) + rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + } + } + +out: + mutex_unlock(&pcm_mutex); + return ret; +} + +/* + * Called by ALSA when the hardware params are set by application. This + * function can also be called multiple times and can allocate buffers + * (using snd_pcm_lib_* ). It's non-atomic. + */ +static int soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_machine *machine = socdev->machine; + int ret = 0; + + mutex_lock(&pcm_mutex); + + /* we don't need to match any AC97 params */ + if (rtd->cpu_dai->type != SND_SOC_DAI_AC97) { + ret = soc_hw_match_params(substream, params); + if (ret < 0) + goto out; + } else { + struct snd_soc_clock_info clk_info; + clk_info.rate = params_rate(params); + ret = soc_get_mclk(rtd, &clk_info); + if (ret < 0) + goto out; + } + + if (rtd->codec_dai->ops.hw_params) { + ret = rtd->codec_dai->ops.hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: can't set codec %s hw params\n", + rtd->codec_dai->name); + goto out; + } + } + + if (rtd->cpu_dai->ops.hw_params) { + ret = rtd->cpu_dai->ops.hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: can't set interface %s hw params\n", + rtd->cpu_dai->name); + goto interface_err; + } + } + + if (platform->pcm_ops->hw_params) { + ret = platform->pcm_ops->hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: can't set platform %s hw params\n", + platform->name); + goto platform_err; + } + } + + if (machine->ops && machine->ops->hw_params) { + ret = machine->ops->hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: machine hw_params failed\n"); + goto machine_err; + } + } + +out: + mutex_unlock(&pcm_mutex); + return ret; + +machine_err: + if (platform->pcm_ops->hw_free) + platform->pcm_ops->hw_free(substream); + +platform_err: + if (rtd->cpu_dai->ops.hw_free) + rtd->cpu_dai->ops.hw_free(substream); + +interface_err: + if (rtd->codec_dai->ops.hw_free) + rtd->codec_dai->ops.hw_free(substream); + + mutex_unlock(&pcm_mutex); + return ret; +} + +/* + * Free's resources allocated by hw_params, can be called multiple times + */ +static int soc_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_machine *machine = socdev->machine; + + mutex_lock(&pcm_mutex); + + /* apply codec digital mute */ + if (!codec->active && rtd->codec_dai->digital_mute) + rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 1); + + /* free any machine hw params */ + if (machine->ops && machine->ops->hw_free) + machine->ops->hw_free(substream); + + /* free any DMA resources */ + if (platform->pcm_ops->hw_free) + platform->pcm_ops->hw_free(substream); + + /* now free hw params for the DAI's */ + if (rtd->codec_dai->ops.hw_free) + rtd->codec_dai->ops.hw_free(substream); + + if (rtd->cpu_dai->ops.hw_free) + rtd->cpu_dai->ops.hw_free(substream); + + mutex_unlock(&pcm_mutex); + return 0; +} + +static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_platform *platform = socdev->platform; + int ret; + + if (rtd->codec_dai->ops.trigger) { + ret = rtd->codec_dai->ops.trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (platform->pcm_ops->trigger) { + ret = platform->pcm_ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (rtd->cpu_dai->ops.trigger) { + ret = rtd->cpu_dai->ops.trigger(substream, cmd); + if (ret < 0) + return ret; + } + return 0; +} + +/* ASoC PCM operations */ +static struct snd_pcm_ops soc_pcm_ops = { + .open = soc_pcm_open, + .close = soc_codec_close, + .hw_params = soc_pcm_hw_params, + .hw_free = soc_pcm_hw_free, + .prepare = soc_pcm_prepare, + .trigger = soc_pcm_trigger, +}; + +#ifdef CONFIG_PM +/* powers down audio subsystem for suspend */ +static int soc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec_device *codec_dev = socdev->codec_dev; + struct snd_soc_codec *codec = socdev->codec; + int i; + + /* mute any active DAC's */ + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; + if (dai->digital_mute && dai->playback.active) + dai->digital_mute(codec, dai, 1); + } + + if (machine->suspend_pre) + machine->suspend_pre(pdev, state); + + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97) + cpu_dai->suspend(pdev, cpu_dai); + if (platform->suspend) + platform->suspend(pdev, cpu_dai); + } + + /* close any waiting streams and save state */ + flush_workqueue(soc_workq); + codec->suspend_dapm_state = codec->dapm_state; + + for(i = 0; i < codec->num_dai; i++) { + char *stream = codec->dai[i].playback.stream_name; + if (stream != NULL) + snd_soc_dapm_stream_event(codec, stream, + SND_SOC_DAPM_STREAM_SUSPEND); + stream = codec->dai[i].capture.stream_name; + if (stream != NULL) + snd_soc_dapm_stream_event(codec, stream, + SND_SOC_DAPM_STREAM_SUSPEND); + } + + if (codec_dev->suspend) + codec_dev->suspend(pdev, state); + + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97) + cpu_dai->suspend(pdev, cpu_dai); + } + + if (machine->suspend_post) + machine->suspend_post(pdev, state); + + return 0; +} + +/* powers up audio subsystem after a suspend */ +static int soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec_device *codec_dev = socdev->codec_dev; + struct snd_soc_codec *codec = socdev->codec; + int i; + + if (machine->resume_pre) + machine->resume_pre(pdev); + + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97) + cpu_dai->resume(pdev, cpu_dai); + } + + if (codec_dev->resume) + codec_dev->resume(pdev); + + for(i = 0; i < codec->num_dai; i++) { + char* stream = codec->dai[i].playback.stream_name; + if (stream != NULL) + snd_soc_dapm_stream_event(codec, stream, + SND_SOC_DAPM_STREAM_RESUME); + stream = codec->dai[i].capture.stream_name; + if (stream != NULL) + snd_soc_dapm_stream_event(codec, stream, + SND_SOC_DAPM_STREAM_RESUME); + } + + /* unmute any active DAC's */ + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; + if (dai->digital_mute && dai->playback.active) + dai->digital_mute(codec, dai, 0); + } + + for(i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97) + cpu_dai->resume(pdev, cpu_dai); + if (platform->resume) + platform->resume(pdev, cpu_dai); + } + + if (machine->resume_post) + machine->resume_post(pdev); + + return 0; +} + +#else +#define soc_suspend NULL +#define soc_resume NULL +#endif + +/* probes a new socdev */ +static int soc_probe(struct platform_device *pdev) +{ + int ret = 0, i; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec_device *codec_dev = socdev->codec_dev; + + if (machine->probe) { + ret = machine->probe(pdev); + if(ret < 0) + return ret; + } + + for (i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->probe) { + ret = cpu_dai->probe(pdev); + if(ret < 0) + goto cpu_dai_err; + } + } + + if (codec_dev->probe) { + ret = codec_dev->probe(pdev); + if(ret < 0) + goto cpu_dai_err; + } + + if (platform->probe) { + ret = platform->probe(pdev); + if(ret < 0) + goto platform_err; + } + + /* DAPM stream work */ + soc_workq = create_workqueue("kdapm"); + if (soc_workq == NULL) + goto work_err; + INIT_WORK(&soc_stream_work, close_delayed_work, socdev); + return 0; + +work_err: + if (platform->remove) + platform->remove(pdev); + +platform_err: + if (codec_dev->remove) + codec_dev->remove(pdev); + +cpu_dai_err: + for (i--; i > 0; i--) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->remove) + cpu_dai->remove(pdev); + } + + if (machine->remove) + machine->remove(pdev); + + return ret; +} + +/* removes a socdev */ +static int soc_remove(struct platform_device *pdev) +{ + int i; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_codec_device *codec_dev = socdev->codec_dev; + + if (soc_workq) + destroy_workqueue(soc_workq); + + if (platform->remove) + platform->remove(pdev); + + if (codec_dev->remove) + codec_dev->remove(pdev); + + for (i = 0; i < machine->num_links; i++) { + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + if (cpu_dai->remove) + cpu_dai->remove(pdev); + } + + if (machine->remove) + machine->remove(pdev); + + return 0; +} + +/* ASoC platform driver */ +static struct platform_driver soc_driver = { + .driver = { + .name = "soc-audio", + }, + .probe = soc_probe, + .remove = soc_remove, + .suspend = soc_suspend, + .resume = soc_resume, +}; + +/* create a new pcm */ +static int soc_new_pcm(struct snd_soc_device *socdev, + struct snd_soc_dai_link *dai_link, int num) +{ + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm *pcm; + char new_name[64]; + int ret = 0, playback = 0, capture = 0; + + rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); + if (rtd == NULL) + return -ENOMEM; + rtd->cpu_dai = cpu_dai; + rtd->codec_dai = codec_dai; + rtd->socdev = socdev; + + /* check client and interface hw capabilities */ + sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name, + get_dai_name(cpu_dai->type), num); + + if (codec_dai->playback.channels_min) + playback = 1; + if (codec_dai->capture.channels_min) + capture = 1; + + ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback, + capture, &pcm); + if (ret < 0) { + printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); + kfree(rtd); + return ret; + } + + pcm->private_data = rtd; + soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; + soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer; + soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl; + soc_pcm_ops.copy = socdev->platform->pcm_ops->copy; + soc_pcm_ops.silence = socdev->platform->pcm_ops->silence; + soc_pcm_ops.ack = socdev->platform->pcm_ops->ack; + soc_pcm_ops.page = socdev->platform->pcm_ops->page; + + if (playback) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); + + if (capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); + + ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm); + if (ret < 0) { + printk(KERN_ERR "asoc: platform pcm constructor failed\n"); + kfree(rtd); + return ret; + } + + pcm->private_free = socdev->platform->pcm_free; + printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, + cpu_dai->name); + return ret; +} + +/* codec register dump */ +static ssize_t codec_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_soc_device *devdata = dev_get_drvdata(dev); + struct snd_soc_codec *codec = devdata->codec; + int i, step = 1, count = 0; + + if (!codec->reg_cache_size) + return 0; + + if (codec->reg_cache_step) + step = codec->reg_cache_step; + + count += sprintf(buf, "%s registers\n", codec->name); + for(i = 0; i < codec->reg_cache_size; i += step) + count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i)); + + return count; +} +static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); + +/** + * snd_soc_new_ac97_codec - initailise AC97 device + * @codec: audio codec + * @ops: AC97 bus operations + * @num: AC97 codec number + * + * Initialises AC97 codec resources for use by ad-hoc devices only. + */ +int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, + struct snd_ac97_bus_ops *ops, int num) +{ + mutex_lock(&codec->mutex); + + codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (codec->ac97 == NULL) { + mutex_unlock(&codec->mutex); + return -ENOMEM; + } + + codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); + if (codec->ac97->bus == NULL) { + kfree(codec->ac97); + codec->ac97 = NULL; + mutex_unlock(&codec->mutex); + return -ENOMEM; + } + + codec->ac97->bus->ops = ops; + codec->ac97->num = num; + mutex_unlock(&codec->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); + +/** + * snd_soc_free_ac97_codec - free AC97 codec device + * @codec: audio codec + * + * Frees AC97 codec device resources. + */ +void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) +{ + mutex_lock(&codec->mutex); + kfree(codec->ac97->bus); + kfree(codec->ac97); + codec->ac97 = NULL; + mutex_unlock(&codec->mutex); +} +EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); + +/** + * snd_soc_update_bits - update codec register bits + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Writes new register value. + * + * Returns 1 for change else 0. + */ +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, + unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + mutex_lock(&io_mutex); + old = snd_soc_read(codec, reg); + new = (old & ~mask) | value; + change = old != new; + if (change) + snd_soc_write(codec, reg, new); + + mutex_unlock(&io_mutex); + return change; +} +EXPORT_SYMBOL_GPL(snd_soc_update_bits); + +/** + * snd_soc_test_bits - test register for change + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Tests a register with a new value and checks if the new value is + * different from the old value. + * + * Returns 1 for change else 0. + */ +int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, + unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + mutex_lock(&io_mutex); + old = snd_soc_read(codec, reg); + new = (old & ~mask) | value; + change = old != new; + mutex_unlock(&io_mutex); + + return change; +} +EXPORT_SYMBOL_GPL(snd_soc_test_bits); + +/** + * snd_soc_get_rate - get int sample rate + * @hwpcmrate: the hardware pcm rate + * + * Returns the audio rate integaer value, else 0. + */ +int snd_soc_get_rate(int hwpcmrate) +{ + int rate = ffs(hwpcmrate) - 1; + + if (rate > ARRAY_SIZE(rates)) + return 0; + return rates[rate]; +} +EXPORT_SYMBOL_GPL(snd_soc_get_rate); + +/** + * snd_soc_new_pcms - create new sound card and pcms + * @socdev: the SoC audio device + * + * Create a new sound card based upon the codec and interface pcms. + * + * Returns 0 for success, else error. + */ +int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char * xid) +{ + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_machine *machine = socdev->machine; + int ret = 0, i; + + mutex_lock(&codec->mutex); + + /* register a sound card */ + codec->card = snd_card_new(idx, xid, codec->owner, 0); + if (!codec->card) { + printk(KERN_ERR "asoc: can't create sound card for codec %s\n", + codec->name); + mutex_unlock(&codec->mutex); + return -ENODEV; + } + + codec->card->dev = socdev->dev; + codec->card->private_data = codec; + strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); + + /* create the pcms */ + for(i = 0; i < machine->num_links; i++) { + ret = soc_new_pcm(socdev, &machine->dai_link[i], i); + if (ret < 0) { + printk(KERN_ERR "asoc: can't create pcm %s\n", + machine->dai_link[i].stream_name); + mutex_unlock(&codec->mutex); + return ret; + } + } + + mutex_unlock(&codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_new_pcms); + +/** + * snd_soc_register_card - register sound card + * @socdev: the SoC audio device + * + * Register a SoC sound card. Also registers an AC97 device if the + * codec is AC97 for ad hoc devices. + * + * Returns 0 for success, else error. + */ +int snd_soc_register_card(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_machine *machine = socdev->machine; + int ret = 0, i, ac97 = 0; + + mutex_lock(&codec->mutex); + for(i = 0; i < machine->num_links; i++) { + if (socdev->machine->dai_link[i].init) + socdev->machine->dai_link[i].init(codec); + if (socdev->machine->dai_link[i].cpu_dai->type == SND_SOC_DAI_AC97) + ac97 = 1; + } + snprintf(codec->card->shortname, sizeof(codec->card->shortname), + "%s", machine->name); + snprintf(codec->card->longname, sizeof(codec->card->longname), + "%s (%s)", machine->name, codec->name); + + ret = snd_card_register(codec->card); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n", + codec->name); + mutex_unlock(&codec->mutex); + return ret; + } + +#ifdef CONFIG_SND_SOC_AC97_BUS + if (ac97) + soc_ac97_dev_register(codec); +#endif + + snd_soc_dapm_sys_add(socdev->dev); + device_create_file(socdev->dev, &dev_attr_codec_reg); + mutex_unlock(&codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_register_card); + +/** + * snd_soc_free_pcms - free sound card and pcms + * @socdev: the SoC audio device + * + * Frees sound card and pcms associated with the socdev. + * Also unregister the codec if it is an AC97 device. + */ +void snd_soc_free_pcms(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + + mutex_lock(&codec->mutex); +#ifdef CONFIG_SND_SOC_AC97_BUS + if (codec->ac97) + soc_ac97_dev_unregister(codec); +#endif + + if (codec->card) + snd_card_free(codec->card); + device_remove_file(socdev->dev, &dev_attr_codec_reg); + mutex_unlock(&codec->mutex); +} +EXPORT_SYMBOL_GPL(snd_soc_free_pcms); + +/** + * snd_soc_set_runtime_hwparams - set the runtime hardware parameters + * @substream: the pcm substream + * @hw: the hardware parameters + * + * Sets the substream runtime hardware parameters. + */ +int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, + const struct snd_pcm_hardware *hw) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + runtime->hw.info = hw->info; + runtime->hw.formats = hw->formats; + runtime->hw.period_bytes_min = hw->period_bytes_min; + runtime->hw.period_bytes_max = hw->period_bytes_max; + runtime->hw.periods_min = hw->periods_min; + runtime->hw.periods_max = hw->periods_max; + runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; + runtime->hw.fifo_size = hw->fifo_size; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); + +/** + * snd_soc_cnew - create new control + * @_template: control template + * @data: control private data + * @lnng_name: control long name + * + * Create a new mixer control from a template control. + * + * Returns 0 for success, else error. + */ +struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, + void *data, char *long_name) +{ + struct snd_kcontrol_new template; + + memcpy(&template, _template, sizeof(template)); + if (long_name) + template.name = long_name; + template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + template.index = 0; + + return snd_ctl_new1(&template, data); +} +EXPORT_SYMBOL_GPL(snd_soc_cnew); + +/** + * snd_soc_info_enum_double - enumerated double mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a double enumerated + * mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = e->shift_l == e->shift_r ? 1 : 2; + uinfo->value.enumerated.items = e->mask; + + if (uinfo->value.enumerated.item > e->mask - 1) + uinfo->value.enumerated.item = e->mask - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); + +/** + * snd_soc_get_enum_double - enumerated double mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + val = snd_soc_read(codec, e->reg); + ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + (val >> e->shift_r) & (bitmask - 1); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); + +/** + * snd_soc_put_enum_double - enumerated double mixer put callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val; + unsigned short mask, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + if (ucontrol->value.enumerated.item[0] > e->mask - 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << e->shift_l; + mask = (bitmask - 1) << e->shift_l; + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->mask - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= (bitmask - 1) << e->shift_r; + } + + return snd_soc_update_bits(codec, e->reg, mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); + +/** + * snd_soc_info_enum_ext - external enumerated single mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about an external enumerated + * single mixer. + * + * Returns 0 for success. + */ +int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->mask; + + if (uinfo->value.enumerated.item > e->mask - 1) + uinfo->value.enumerated.item = e->mask - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); + +/** + * snd_soc_info_volsw_ext - external single mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single external mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int mask = kcontrol->private_value; + + uinfo->type = + mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); + +/** + * snd_soc_info_bool_ext - external single boolean mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single boolean external mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_bool_ext(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; +} +EXPORT_SYMBOL_GPL(snd_soc_info_bool_ext); + +/** + * snd_soc_info_volsw - single mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int rshift = (kcontrol->private_value >> 12) & 0x0f; + + uinfo->type = + mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = shift == rshift ? 1 : 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw); + +/** + * snd_soc_get_volsw - single mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int rshift = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + if (shift != rshift) + ucontrol->value.integer.value[1] = + (snd_soc_read(codec, reg) >> rshift) & mask; + if (invert) { + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + if (shift != rshift) + ucontrol->value.integer.value[1] = + mask - ucontrol->value.integer.value[1]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw); + +/** + * snd_soc_put_volsw - single mixer put callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int rshift = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; + int err; + unsigned short val, val2, val_mask; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val_mask = mask << shift; + val = val << shift; + if (shift != rshift) { + val2 = (ucontrol->value.integer.value[1] & mask); + if (invert) + val2 = mask - val2; + val_mask |= mask << rshift; + val |= val2 << rshift; + } + err = snd_soc_update_bits(codec, reg, val_mask, val); + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw); + +/** + * snd_soc_info_volsw_2r - double mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a double mixer control that + * spans 2 codec registers. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int mask = (kcontrol->private_value >> 12) & 0xff; + + uinfo->type = + mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); + +/** + * snd_soc_get_volsw_2r - double mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a double mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 24) & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int mask = (kcontrol->private_value >> 12) & 0xff; + int invert = (kcontrol->private_value >> 20) & 0x01; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + ucontrol->value.integer.value[1] = + (snd_soc_read(codec, reg2) >> shift) & mask; + if (invert) { + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = + mask - ucontrol->value.integer.value[1]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r); + +/** + * snd_soc_put_volsw_2r - double mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a double mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 24) & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int mask = (kcontrol->private_value >> 12) & 0xff; + int invert = (kcontrol->private_value >> 20) & 0x01; + int err; + unsigned short val, val2, val_mask; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] & mask); + val2 = (ucontrol->value.integer.value[1] & mask); + + if (invert) { + val = mask - val; + val2 = mask - val2; + } + + val = val << shift; + val2 = val2 << shift; + + if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0) + return err; + + err = snd_soc_update_bits(codec, reg2, val_mask, val2); + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); + +static int __devinit snd_soc_init(void) +{ + printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION); + return platform_driver_register(&soc_driver); +} + +static void snd_soc_exit(void) +{ + platform_driver_unregister(&soc_driver); +} + +module_init(snd_soc_init); +module_exit(snd_soc_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("ALSA SoC Core"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 2b97eabc09f42d0f63e8053636e34e1afa0d604e Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:32:18 +0200 Subject: [ALSA] ASoC: dynamic audio power management (DAPM) This patch adds Dynamic Audio Power Management (DAPM) to ASoC. Dynamic Audio Power Management (DAPM) is designed to allow portable and handheld Linux devices to use the minimum amount of power within the audio subsystem at all times. It is independent of other kernel PM and as such, can easily co-exist with the other PM systems. DAPM is also completely transparent to all user space applications as all power switching is done within the ASoC core. No code changes or recompiling are required for user space applications. DAPM makes power switching decisions based upon any audio stream (capture/playback) activity and audio mixer settings within the device. DAPM spans the whole machine. It covers power control within the entire audio subsystem, this includes internal codec power blocks and machine level power systems. There are 4 power domains within DAPM:- 1. Codec domain - VREF, VMID (core codec and audio power) Usually controlled at codec probe/remove and suspend/resume, although can be set at stream time if power is not needed for sidetone, etc. 2. Platform/Machine domain - physically connected inputs and outputs Is platform/machine and user action specific, is configured by the machine driver and responds to asynchronous events e.g when HP are inserted 3. Path domain - audio subsystem signal paths Automatically set when mixer and mux settings are changed by the user. e.g. alsamixer, amixer. 4. Stream domain - DAC's and ADC's. Enabled and disabled when stream playback/capture is started and stopped respectively. e.g. aplay, arecord. All DAPM power switching decisions are made automatically by consulting an audio routing map of the whole machine. This map is specific to each machine and consists of the interconnections between every audio component (including internal codec components). Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-dapm.c | 1327 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1327 insertions(+) create mode 100644 sound/soc/soc-dapm.c diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c new file mode 100644 index 000000000000..2c2c27f4e9c0 --- /dev/null +++ b/sound/soc/soc-dapm.c @@ -0,0 +1,1327 @@ +/* + * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * 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. + * + * Revision history + * 12th Aug 2005 Initial version. + * 25th Oct 2005 Implemented path power domain. + * 18th Dec 2005 Implemented machine and stream level power domain. + * + * Features: + * o Changes power status of internal codec blocks depending on the + * dynamic configuration of codec internal audio paths and active + * DAC's/ADC's. + * o Platform power domain - can support external components i.e. amps and + * mic/meadphone insertion events. + * o Automatic Mic Bias support + * o Jack insertion power event initiation - e.g. hp insertion will enable + * sinks, dacs, etc + * o Delayed powerdown of audio susbsytem to reduce pops between a quick + * device reopen. + * + * Todo: + * o DAPM power change sequencing - allow for configurable per + * codec sequences. + * o Support for analogue bias optimisation. + * o Support for reduced codec oversampling rates. + * o Support for reduced codec bias currents. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug */ +#define DAPM_DEBUG 0 +#if DAPM_DEBUG +#define dump_dapm(codec, action) dbg_dump_dapm(codec, action) +#define dbg(format, arg...) printk(format, ## arg) +#else +#define dump_dapm(codec, action) +#define dbg(format, arg...) +#endif + +#define POP_DEBUG 0 +#if POP_DEBUG +#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */ +#define pop_wait(time) schedule_timeout_interruptible(msecs_to_jiffies(time)) +#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME) +#else +#define pop_dbg(format, arg...) +#define pop_wait(time) +#endif + +/* dapm power sequences - make this per codec in the future */ +static int dapm_up_seq[] = { + snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, + snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga, + snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post +}; +static int dapm_down_seq[] = { + snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, + snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, + snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post +}; + +static int dapm_status = 1; +module_param(dapm_status, int, 0); +MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); + +/* create a new dapm widget */ +static struct snd_soc_dapm_widget *dapm_cnew_widget( + const struct snd_soc_dapm_widget *_widget) +{ + struct snd_soc_dapm_widget* widget; + widget = kmalloc(sizeof(struct snd_soc_dapm_widget), GFP_KERNEL); + if (!widget) + return NULL; + + memcpy(widget, _widget, sizeof(struct snd_soc_dapm_widget)); + return widget; +} + +/* set up initial codec paths */ +static void dapm_set_path_status(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_path *p, int i) +{ + switch (w->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: { + int val; + int reg = w->kcontrols[i].private_value & 0xff; + int shift = (w->kcontrols[i].private_value >> 8) & 0x0f; + int mask = (w->kcontrols[i].private_value >> 16) & 0xff; + int invert = (w->kcontrols[i].private_value >> 24) & 0x01; + + val = snd_soc_read(w->codec, reg); + val = (val >> shift) & mask; + + if ((invert && !val) || (!invert && val)) + p->connect = 1; + else + p->connect = 0; + } + break; + case snd_soc_dapm_mux: { + struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; + int val, item, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + val = snd_soc_read(w->codec, e->reg); + item = (val >> e->shift_l) & (bitmask - 1); + + p->connect = 0; + for (i = 0; i < e->mask; i++) { + if (!(strcmp(p->name, e->texts[i])) && item == i) + p->connect = 1; + } + } + break; + /* does not effect routing - always connected */ + case snd_soc_dapm_pga: + case snd_soc_dapm_output: + case snd_soc_dapm_adc: + case snd_soc_dapm_input: + case snd_soc_dapm_dac: + case snd_soc_dapm_micbias: + case snd_soc_dapm_vmid: + p->connect = 1; + break; + /* does effect routing - dynamically connected */ + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_spk: + case snd_soc_dapm_line: + case snd_soc_dapm_pre: + case snd_soc_dapm_post: + p->connect = 0; + break; + } +} + +/* connect mux widget to it's interconnecting audio paths */ +static int dapm_connect_mux(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, + struct snd_soc_dapm_path *path, const char *control_name, + const struct snd_kcontrol_new *kcontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int i; + + for (i = 0; i < e->mask; i++) { + if (!(strcmp(control_name, e->texts[i]))) { + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &dest->sources); + list_add(&path->list_source, &src->sinks); + path->name = (char*)e->texts[i]; + dapm_set_path_status(dest, path, 0); + return 0; + } + } + + return -ENODEV; +} + +/* connect mixer widget to it's interconnecting audio paths */ +static int dapm_connect_mixer(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, + struct snd_soc_dapm_path *path, const char *control_name) +{ + int i; + + /* search for mixer kcontrol */ + for (i = 0; i < dest->num_kcontrols; i++) { + if (!strcmp(control_name, dest->kcontrols[i].name)) { + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &dest->sources); + list_add(&path->list_source, &src->sinks); + path->name = dest->kcontrols[i].name; + dapm_set_path_status(dest, path, i); + return 0; + } + } + return -ENODEV; +} + +/* update dapm codec register bits */ +static int dapm_update_bits(struct snd_soc_dapm_widget *widget) +{ + int change, power; + unsigned short old, new; + struct snd_soc_codec *codec = widget->codec; + + /* check for valid widgets */ + if (widget->reg < 0 || widget->id == snd_soc_dapm_input || + widget->id == snd_soc_dapm_output || + widget->id == snd_soc_dapm_hp || + widget->id == snd_soc_dapm_mic || + widget->id == snd_soc_dapm_line || + widget->id == snd_soc_dapm_spk) + return 0; + + power = widget->power; + if (widget->invert) + power = (power ? 0:1); + + old = snd_soc_read(codec, widget->reg); + new = (old & ~(0x1 << widget->shift)) | (power << widget->shift); + + change = old != new; + if (change) { + pop_dbg("pop test %s : %s in %d ms\n", widget->name, + widget->power ? "on" : "off", POP_TIME); + snd_soc_write(codec, widget->reg, new); + pop_wait(POP_TIME); + } + dbg("reg old %x new %x change %d\n", old, new, change); + return change; +} + +/* ramps the volume up or down to minimise pops before or after a + * DAPM power event */ +static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) +{ + const struct snd_kcontrol_new *k = widget->kcontrols; + + if (widget->muted && !power) + return 0; + if (!widget->muted && power) + return 0; + + if (widget->num_kcontrols && k) { + int reg = k->private_value & 0xff; + int shift = (k->private_value >> 8) & 0x0f; + int mask = (k->private_value >> 16) & 0xff; + int invert = (k->private_value >> 24) & 0x01; + + if (power) { + int i; + /* power up has happended, increase volume to last level */ + if (invert) { + for (i = mask; i > widget->saved_value; i--) + snd_soc_update_bits(widget->codec, reg, mask, i); + } else { + for (i = 0; i < widget->saved_value; i++) + snd_soc_update_bits(widget->codec, reg, mask, i); + } + widget->muted = 0; + } else { + /* power down is about to occur, decrease volume to mute */ + int val = snd_soc_read(widget->codec, reg); + int i = widget->saved_value = (val >> shift) & mask; + if (invert) { + for (; i < mask; i++) + snd_soc_update_bits(widget->codec, reg, mask, i); + } else { + for (; i > 0; i--) + snd_soc_update_bits(widget->codec, reg, mask, i); + } + widget->muted = 1; + } + } + return 0; +} + +/* create new dapm mixer control */ +static int dapm_new_mixer(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *w) +{ + int i, ret = 0; + char name[32]; + struct snd_soc_dapm_path *path; + + /* add kcontrol */ + for (i = 0; i < w->num_kcontrols; i++) { + + /* match name */ + list_for_each_entry(path, &w->sources, list_sink) { + + /* mixer/mux paths name must match control name */ + if (path->name != (char*)w->kcontrols[i].name) + continue; + + /* add dapm control with long name */ + snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name); + path->long_name = kstrdup (name, GFP_KERNEL); + if (path->long_name == NULL) + return -ENOMEM; + + path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, + path->long_name); + ret = snd_ctl_add(codec->card, path->kcontrol); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n", + path->long_name); + kfree(path->long_name); + path->long_name = NULL; + return ret; + } + } + } + return ret; +} + +/* create new dapm mux control */ +static int dapm_new_mux(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *path = NULL; + struct snd_kcontrol *kcontrol; + int ret = 0; + + if (!w->num_kcontrols) { + printk(KERN_ERR "asoc: mux %s has no controls\n", w->name); + return -EINVAL; + } + + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + ret = snd_ctl_add(codec->card, kcontrol); + if (ret < 0) + goto err; + + list_for_each_entry(path, &w->sources, list_sink) + path->kcontrol = kcontrol; + + return ret; + +err: + printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name); + return ret; +} + +/* create new dapm volume control */ +static int dapm_new_pga(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *w) +{ + struct snd_kcontrol *kcontrol; + int ret = 0; + + if (!w->num_kcontrols) + return -EINVAL; + + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + ret = snd_ctl_add(codec->card, kcontrol); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name); + return ret; + } + + return ret; +} + +/* reset 'walked' bit for each dapm path */ +static inline void dapm_clear_walk(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_path *p; + + list_for_each_entry(p, &codec->dapm_paths, list) + p->walked = 0; +} + +/* + * Recursively check for a completed path to an active or physically connected + * output widget. Returns number of complete paths. + */ +static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_dapm_path *path; + int con = 0; + + if (widget->id == snd_soc_dapm_adc && widget->active) + return 1; + + if (widget->connected) { + /* connected pin ? */ + if (widget->id == snd_soc_dapm_output && !widget->ext) + return 1; + + /* connected jack or spk ? */ + if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || + widget->id == snd_soc_dapm_line) + return 1; + } + + list_for_each_entry(path, &widget->sinks, list_source) { + if (path->walked) + continue; + + if (path->sink && path->connect) { + path->walked = 1; + con += is_connected_output_ep(path->sink); + } + } + + return con; +} + +/* + * Recursively check for a completed path to an active or physically connected + * input widget. Returns number of complete paths. + */ +static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_dapm_path *path; + int con = 0; + + /* active stream ? */ + if (widget->id == snd_soc_dapm_dac && widget->active) + return 1; + + if (widget->connected) { + /* connected pin ? */ + if (widget->id == snd_soc_dapm_input && !widget->ext) + return 1; + + /* connected VMID/Bias for lower pops */ + if (widget->id == snd_soc_dapm_vmid) + return 1; + + /* connected jack ? */ + if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line) + return 1; + } + + list_for_each_entry(path, &widget->sources, list_sink) { + if (path->walked) + continue; + + if (path->source && path->connect) { + path->walked = 1; + con += is_connected_input_ep(path->source); + } + } + + return con; +} + +/* + * Scan each dapm widget for complete audio path. + * A complete path is a route that has valid endpoints i.e.:- + * + * o DAC to output pin. + * o Input Pin to ADC. + * o Input pin to Output pin (bypass, sidetone) + * o DAC to ADC (loopback). + */ +int dapm_power_widgets(struct snd_soc_codec *codec, int event) +{ + struct snd_soc_dapm_widget *w; + int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power; + + /* do we have a sequenced stream event */ + if (event == SND_SOC_DAPM_STREAM_START) { + c = ARRAY_SIZE(dapm_up_seq); + seq = dapm_up_seq; + } else if (event == SND_SOC_DAPM_STREAM_STOP) { + c = ARRAY_SIZE(dapm_down_seq); + seq = dapm_down_seq; + } + + for(i = 0; i < c; i++) { + list_for_each_entry(w, &codec->dapm_widgets, list) { + + /* is widget in stream order */ + if (seq && seq[i] && w->id != seq[i]) + continue; + + /* vmid - no action */ + if (w->id == snd_soc_dapm_vmid) + continue; + + /* active ADC */ + if (w->id == snd_soc_dapm_adc && w->active) { + in = is_connected_input_ep(w); + dapm_clear_walk(w->codec); + w->power = (in != 0) ? 1 : 0; + dapm_update_bits(w); + continue; + } + + /* active DAC */ + if (w->id == snd_soc_dapm_dac && w->active) { + out = is_connected_output_ep(w); + dapm_clear_walk(w->codec); + w->power = (out != 0) ? 1 : 0; + dapm_update_bits(w); + continue; + } + + /* programmable gain/attenuation */ + if (w->id == snd_soc_dapm_pga) { + int on; + in = is_connected_input_ep(w); + dapm_clear_walk(w->codec); + out = is_connected_output_ep(w); + dapm_clear_walk(w->codec); + w->power = on = (out != 0 && in != 0) ? 1 : 0; + + if (!on) + dapm_set_pga(w, on); /* lower volume to reduce pops */ + dapm_update_bits(w); + if (on) + dapm_set_pga(w, on); /* restore volume from zero */ + + continue; + } + + /* pre and post event widgets */ + if (w->id == snd_soc_dapm_pre) { + if (!w->event) + continue; + + if (event == SND_SOC_DAPM_STREAM_START) { + ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + if (ret < 0) + return ret; + } else if (event == SND_SOC_DAPM_STREAM_STOP) { + ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + if (ret < 0) + return ret; + } + continue; + } + if (w->id == snd_soc_dapm_post) { + if (!w->event) + continue; + + if (event == SND_SOC_DAPM_STREAM_START) { + ret = w->event(w, SND_SOC_DAPM_POST_PMU); + if (ret < 0) + return ret; + } else if (event == SND_SOC_DAPM_STREAM_STOP) { + ret = w->event(w, SND_SOC_DAPM_POST_PMD); + if (ret < 0) + return ret; + } + continue; + } + + /* all other widgets */ + in = is_connected_input_ep(w); + dapm_clear_walk(w->codec); + out = is_connected_output_ep(w); + dapm_clear_walk(w->codec); + power = (out != 0 && in != 0) ? 1 : 0; + power_change = (w->power == power) ? 0: 1; + w->power = power; + + /* call any power change event handlers */ + if (power_change) { + if (w->event) { + dbg("power %s event for %s flags %x\n", + w->power ? "on" : "off", w->name, w->event_flags); + if (power) { + /* power up event */ + if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { + ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + if (ret < 0) + return ret; + } + dapm_update_bits(w); + if (w->event_flags & SND_SOC_DAPM_POST_PMU){ + ret = w->event(w, SND_SOC_DAPM_POST_PMU); + if (ret < 0) + return ret; + } + } else { + /* power down event */ + if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { + ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + if (ret < 0) + return ret; + } + dapm_update_bits(w); + if (w->event_flags & SND_SOC_DAPM_POST_PMD) { + ret = w->event(w, SND_SOC_DAPM_POST_PMD); + if (ret < 0) + return ret; + } + } + } else + /* no event handler */ + dapm_update_bits(w); + } + } + } + + return ret; +} + +#if DAPM_DEBUG +static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + int in, out; + + printk("DAPM %s %s\n", codec->name, action); + + list_for_each_entry(w, &codec->dapm_widgets, list) { + + /* only display widgets that effect routing */ + switch (w->id) { + case snd_soc_dapm_pre: + case snd_soc_dapm_post: + case snd_soc_dapm_vmid: + continue; + case snd_soc_dapm_mux: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + case snd_soc_dapm_switch: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_spk: + case snd_soc_dapm_line: + case snd_soc_dapm_micbias: + case snd_soc_dapm_dac: + case snd_soc_dapm_adc: + case snd_soc_dapm_pga: + case snd_soc_dapm_mixer: + if (w->name) { + in = is_connected_input_ep(w); + dapm_clear_walk(w->codec); + out = is_connected_output_ep(w); + dapm_clear_walk(w->codec); + printk("%s: %s in %d out %d\n", w->name, + w->power ? "On":"Off",in, out); + + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connect) + printk(" in %s %s\n", p->name ? p->name : "static", + p->source->name); + } + list_for_each_entry(p, &w->sinks, list_source) { + p = list_entry(lp, struct snd_soc_dapm_path, list_source); + if (p->connect) + printk(" out %s %s\n", p->name ? p->name : "static", + p->sink->name); + } + } + break; + } + } +} +#endif + +/* test and update the power status of a mux widget */ +int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int mask, int val, struct soc_enum* e) +{ + struct snd_soc_dapm_path *path; + int found = 0; + + if (widget->id != snd_soc_dapm_mux) + return -ENODEV; + + if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) + return 0; + + /* find dapm widget path assoc with kcontrol */ + list_for_each_entry(path, &widget->codec->dapm_paths, list) { + if (path->kcontrol != kcontrol) + continue; + + if (!path->name || ! e->texts[val]) + continue; + + found = 1; + /* we now need to match the string in the enum to the path */ + if (!(strcmp(path->name, e->texts[val]))) + path->connect = 1; /* new connection */ + else + path->connect = 0; /* old connection must be powered down */ + } + + if (found) + dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + + return 0; +} +EXPORT_SYMBOL_GPL(dapm_mux_update_power); + +/* test and update the power status of a mixer widget */ +int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert) +{ + struct snd_soc_dapm_path *path; + int found = 0; + + if (widget->id != snd_soc_dapm_mixer) + return -ENODEV; + + if (!snd_soc_test_bits(widget->codec, reg, val_mask, val)) + return 0; + + /* find dapm widget path assoc with kcontrol */ + list_for_each_entry(path, &widget->codec->dapm_paths, list) { + if (path->kcontrol != kcontrol) + continue; + + /* found, now check type */ + found = 1; + if (val) + /* new connection */ + path->connect = invert ? 0:1; + else + /* old connection must be powered down */ + path->connect = invert ? 1:0; + break; + } + + if (found) + dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + + return 0; +} +EXPORT_SYMBOL_GPL(dapm_mixer_update_power); + +/* show dapm widget status in sys fs */ +static ssize_t dapm_widget_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_soc_device *devdata = dev_get_drvdata(dev); + struct snd_soc_codec *codec = devdata->codec; + struct snd_soc_dapm_widget *w; + int count = 0; + char *state = "not set"; + + list_for_each_entry(w, &codec->dapm_widgets, list) { + + /* only display widgets that burnm power */ + switch (w->id) { + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_spk: + case snd_soc_dapm_line: + case snd_soc_dapm_micbias: + case snd_soc_dapm_dac: + case snd_soc_dapm_adc: + case snd_soc_dapm_pga: + case snd_soc_dapm_mixer: + if (w->name) + count += sprintf(buf + count, "%s: %s\n", + w->name, w->power ? "On":"Off"); + break; + default: + break; + } + } + + switch(codec->dapm_state){ + case SNDRV_CTL_POWER_D0: + state = "D0"; + break; + case SNDRV_CTL_POWER_D1: + state = "D1"; + break; + case SNDRV_CTL_POWER_D2: + state = "D2"; + break; + case SNDRV_CTL_POWER_D3hot: + state = "D3hot"; + break; + case SNDRV_CTL_POWER_D3cold: + state = "D3cold"; + break; + } + count += sprintf(buf + count, "PM State: %s\n", state); + + return count; +} + +static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); + +int snd_soc_dapm_sys_add(struct device *dev) +{ + int ret = 0; + + if (dapm_status) + ret = device_create_file(dev, &dev_attr_dapm_widget); + + return ret; +} + +static void snd_soc_dapm_sys_remove(struct device *dev) +{ + if (dapm_status) + device_remove_file(dev, &dev_attr_dapm_widget); +} + +/* free all dapm widgets and resources */ +void dapm_free_widgets(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_widget *w, *next_w; + struct snd_soc_dapm_path *p, *next_p; + + list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) { + list_del(&w->list); + kfree(w); + } + + list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) { + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } +} + +/** + * snd_soc_dapm_sync_endpoints - scan and power dapm paths + * @codec: audio codec + * + * Walks all dapm audio paths and powers widgets according to their + * stream or path usage. + * + * Returns 0 for success. + */ +int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec) +{ + return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints); + +/** + * snd_soc_dapm_connect_input - connect dapm widgets + * @codec: audio codec + * @sink: name of target widget + * @control: mixer control name + * @source: name of source name + * + * Connects 2 dapm widgets together via a named audio path. The sink is + * the widget receiving the audio signal, whilst the source is the sender + * of the audio signal. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, + const char * control, const char *source) +{ + struct snd_soc_dapm_path *path; + struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + int ret = 0; + + /* find src and dest widgets */ + list_for_each_entry(w, &codec->dapm_widgets, list) { + + if (!wsink && !(strcmp(w->name, sink))) { + wsink = w; + continue; + } + if (!wsource && !(strcmp(w->name, source))) { + wsource = w; + } + } + + if (wsource == NULL || wsink == NULL) + return -ENODEV; + + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); + if (!path) + return -ENOMEM; + + path->source = wsource; + path->sink = wsink; + INIT_LIST_HEAD(&path->list); + INIT_LIST_HEAD(&path->list_source); + INIT_LIST_HEAD(&path->list_sink); + + /* check for external widgets */ + if (wsink->id == snd_soc_dapm_input) { + if (wsource->id == snd_soc_dapm_micbias || + wsource->id == snd_soc_dapm_mic || + wsink->id == snd_soc_dapm_line) + wsink->ext = 1; + } + if (wsource->id == snd_soc_dapm_output) { + if (wsink->id == snd_soc_dapm_spk || + wsink->id == snd_soc_dapm_hp || + wsink->id == snd_soc_dapm_line) + wsource->ext = 1; + } + + /* connect static paths */ + if (control == NULL) { + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &wsink->sources); + list_add(&path->list_source, &wsource->sinks); + path->connect = 1; + return 0; + } + + /* connect dynamic paths */ + switch(wsink->id) { + case snd_soc_dapm_adc: + case snd_soc_dapm_dac: + case snd_soc_dapm_pga: + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + case snd_soc_dapm_vmid: + case snd_soc_dapm_pre: + case snd_soc_dapm_post: + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &wsink->sources); + list_add(&path->list_source, &wsource->sinks); + path->connect = 1; + return 0; + case snd_soc_dapm_mux: + ret = dapm_connect_mux(codec, wsource, wsink, path, control, + &wsink->kcontrols[0]); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + ret = dapm_connect_mixer(codec, wsource, wsink, path, control); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_line: + case snd_soc_dapm_spk: + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &wsink->sources); + list_add(&path->list_source, &wsource->sinks); + path->connect = 0; + return 0; + } + return 0; + +err: + printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source, + control, sink); + kfree(path); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input); + +/** + * snd_soc_dapm_new_widgets - add new dapm widgets + * @codec: audio codec + * + * Checks the codec for any new dapm widgets and creates them if found. + * + * Returns 0 for success. + */ +int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_widget *w; + + mutex_lock(&codec->mutex); + list_for_each_entry(w, &codec->dapm_widgets, list) + { + if (w->new) + continue; + + switch(w->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + dapm_new_mixer(codec, w); + break; + case snd_soc_dapm_mux: + dapm_new_mux(codec, w); + break; + case snd_soc_dapm_adc: + case snd_soc_dapm_dac: + case snd_soc_dapm_pga: + dapm_new_pga(codec, w); + break; + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + case snd_soc_dapm_spk: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_line: + case snd_soc_dapm_vmid: + case snd_soc_dapm_pre: + case snd_soc_dapm_post: + break; + } + w->new = 1; + } + + dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&codec->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); + +/** + * snd_soc_dapm_get_volsw - dapm mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a dapm mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int rshift = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; + + /* return the saved value if we are powered down */ + if (widget->id == snd_soc_dapm_pga && !widget->power) { + ucontrol->value.integer.value[0] = widget->saved_value; + return 0; + } + + ucontrol->value.integer.value[0] = + (snd_soc_read(widget->codec, reg) >> shift) & mask; + if (shift != rshift) + ucontrol->value.integer.value[1] = + (snd_soc_read(widget->codec, reg) >> rshift) & mask; + if (invert) { + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + if (shift != rshift) + ucontrol->value.integer.value[1] = + mask - ucontrol->value.integer.value[1]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); + +/** + * snd_soc_dapm_put_volsw - dapm mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a dapm mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int rshift = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; + unsigned short val, val2, val_mask; + int ret; + + val = (ucontrol->value.integer.value[0] & mask); + + if (invert) + val = mask - val; + val_mask = mask << shift; + val = val << shift; + if (shift != rshift) { + val2 = (ucontrol->value.integer.value[1] & mask); + if (invert) + val2 = mask - val2; + val_mask |= mask << rshift; + val |= val2 << rshift; + } + + mutex_lock(&widget->codec->mutex); + widget->value = val; + + /* save volume value if the widget is powered down */ + if (widget->id == snd_soc_dapm_pga && !widget->power) { + widget->saved_value = val; + mutex_unlock(&widget->codec->mutex); + return 1; + } + + dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert); + if (widget->event) { + if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { + ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); + if (ret < 0) + goto out; + } + ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); + if (widget->event_flags & SND_SOC_DAPM_POST_REG) + ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + } else + ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); + +out: + mutex_unlock(&widget->codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); + +/** + * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a dapm enumerated double mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + val = snd_soc_read(widget->codec, e->reg); + ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + (val >> e->shift_r) & (bitmask - 1); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); + +/** + * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a dapm enumerated double mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val, mux; + unsigned short mask, bitmask; + int ret = 0; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + if (ucontrol->value.enumerated.item[0] > e->mask - 1) + return -EINVAL; + mux = ucontrol->value.enumerated.item[0]; + val = mux << e->shift_l; + mask = (bitmask - 1) << e->shift_l; + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->mask - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= (bitmask - 1) << e->shift_r; + } + + mutex_lock(&widget->codec->mutex); + widget->value = val; + dapm_mux_update_power(widget, kcontrol, mask, mux, e); + if (widget->event) { + if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { + ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); + if (ret < 0) + goto out; + } + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + if (widget->event_flags & SND_SOC_DAPM_POST_REG) + ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + } else + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + +out: + mutex_unlock(&widget->codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); + +/** + * snd_soc_dapm_new_control - create new dapm control + * @codec: audio codec + * @widget: widget template + * + * Creates a new dapm control based upon the template. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_new_control(struct snd_soc_codec *codec, + const struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_dapm_widget *w; + + if ((w = dapm_cnew_widget(widget)) == NULL) + return -ENOMEM; + + w->codec = codec; + INIT_LIST_HEAD(&w->sources); + INIT_LIST_HEAD(&w->sinks); + INIT_LIST_HEAD(&w->list); + list_add(&w->list, &codec->dapm_widgets); + + /* machine layer set ups unconnected pins and insertions */ + w->connected = 1; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); + +/** + * snd_soc_dapm_stream_event - send a stream event to the dapm core + * @codec: audio codec + * @stream: stream name + * @event: stream event + * + * Sends a stream event to the dapm core. The core then makes any + * necessary widget power changes. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, + char *stream, int event) +{ + struct snd_soc_dapm_widget *w; + + mutex_lock(&codec->mutex); + list_for_each_entry(w, &codec->dapm_widgets, list) + { + if (!w->sname) + continue; + dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname, + stream, event); + if (strstr(w->sname, stream)) { + switch(event) { + case SND_SOC_DAPM_STREAM_START: + w->active = 1; + break; + case SND_SOC_DAPM_STREAM_STOP: + w->active = 0; + break; + case SND_SOC_DAPM_STREAM_SUSPEND: + if (w->active) + w->suspend = 1; + w->active = 0; + break; + case SND_SOC_DAPM_STREAM_RESUME: + if (w->suspend) { + w->active = 1; + w->suspend = 0; + } + break; + case SND_SOC_DAPM_STREAM_PAUSE_PUSH: + break; + case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: + break; + } + } + } + mutex_unlock(&codec->mutex); + + dapm_power_widgets(codec, event); + dump_dapm(codec, __FUNCTION__); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); + +/** + * snd_soc_dapm_set_endpoint - set audio endpoint status + * @codec: audio codec + * @endpoint: audio signal endpoint (or start point) + * @status: point status + * + * Set audio endpoint status - connected or disconnected. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, + char *endpoint, int status) +{ + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, &codec->dapm_widgets, list) { + if (!strcmp(w->name, endpoint)) { + w->connected = status; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint); + +/** + * snd_soc_dapm_free - free dapm resources + * @socdev: SoC device + * + * Free all dapm widgets and resources. + */ +void snd_soc_dapm_free(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + + snd_soc_dapm_sys_remove(socdev->dev); + dapm_free_widgets(codec); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_free); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From a3288176de3fdd439d9bca0a0b9ca749c12ac5ac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 6 Oct 2006 18:33:55 +0200 Subject: [ALSA] ASoC: Build files This patch adds support for building the ASoC core and the dynamic audio power management support. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/Kconfig | 2 ++ sound/Makefile | 2 +- sound/soc/Kconfig | 19 +++++++++++++++++++ sound/soc/Makefile | 4 ++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 sound/soc/Kconfig create mode 100644 sound/soc/Makefile diff --git a/sound/Kconfig b/sound/Kconfig index 9d77300746c6..97532bbc2ccb 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -76,6 +76,8 @@ source "sound/sparc/Kconfig" source "sound/parisc/Kconfig" +source "sound/soc/Kconfig" + endmenu menu "Open Sound System" diff --git a/sound/Makefile b/sound/Makefile index 9aee54c4882d..b7c7fb7c24c8 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ -obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ +obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig new file mode 100644 index 000000000000..200709f4b708 --- /dev/null +++ b/sound/soc/Kconfig @@ -0,0 +1,19 @@ +# +# SoC audio configuration +# + +menu "SoC audio support" + depends on SND!=n + +config SND_SOC + tristate "SoC audio support" + ---help--- + + If you want SoC support, you should say Y here and also to the + specific driver for your SoC below. You will also need to select the + specific codec(s) attached to the SoC + + This SoC audio support can also be built as a module. If so, the module + will be called snd-soc-core. + +endmenu diff --git a/sound/soc/Makefile b/sound/soc/Makefile new file mode 100644 index 000000000000..b211ee63fd76 --- /dev/null +++ b/sound/soc/Makefile @@ -0,0 +1,4 @@ +snd-soc-core-objs := soc-core.o soc-dapm.o + +obj-$(CONFIG_SND_SOC) += snd-soc-core.o + -- cgit v1.2.3-59-g8ed1b From eb1a6af39b70375d93ed25e7c916f64463e00614 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 6 Oct 2006 18:34:51 +0200 Subject: [ALSA] ASoC: documentation & maintainer This patch adds documentation describing the ASoC architecture and a maintainer entry for ASoC. The documentation includes the following files:- codec.txt: Codec driver internals. DAI.txt: Description of Digital Audio Interface standards and how to configure a DAI within your codec and CPU DAI drivers. dapm.txt: Dynamic Audio Power Management. platform.txt: Platform audio DMA and DAI. machine.txt: Machine driver internals. pop_clicks.txt: How to minimise audio artifacts. clocking.txt: ASoC clocking for best power performance. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/soc/DAI.txt | 380 +++++++++++++++++++++++++++ Documentation/sound/alsa/soc/clocking.txt | 309 ++++++++++++++++++++++ Documentation/sound/alsa/soc/codec.txt | 232 ++++++++++++++++ Documentation/sound/alsa/soc/dapm.txt | 297 +++++++++++++++++++++ Documentation/sound/alsa/soc/machine.txt | 114 ++++++++ Documentation/sound/alsa/soc/overview.txt | 83 ++++++ Documentation/sound/alsa/soc/platform.txt | 58 ++++ Documentation/sound/alsa/soc/pops_clicks.txt | 52 ++++ MAINTAINERS | 6 + 9 files changed, 1531 insertions(+) create mode 100644 Documentation/sound/alsa/soc/DAI.txt create mode 100644 Documentation/sound/alsa/soc/clocking.txt create mode 100644 Documentation/sound/alsa/soc/codec.txt create mode 100644 Documentation/sound/alsa/soc/dapm.txt create mode 100644 Documentation/sound/alsa/soc/machine.txt create mode 100644 Documentation/sound/alsa/soc/overview.txt create mode 100644 Documentation/sound/alsa/soc/platform.txt create mode 100644 Documentation/sound/alsa/soc/pops_clicks.txt diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/alsa/soc/DAI.txt new file mode 100644 index 000000000000..919de76bab8d --- /dev/null +++ b/Documentation/sound/alsa/soc/DAI.txt @@ -0,0 +1,380 @@ +ASoC currently supports the three main Digital Audio Interfaces (DAI) found on +SoC controllers and portable audio CODECS today, namely AC97, I2S and PCM. + + +AC97 +==== + + AC97 is a five wire interface commonly found on many PC sound cards. It is +now also popular in many portable devices. This DAI has a reset line and time +multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines. +The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the +frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97 +frame is 21uS long and is divided into 13 time slots. + +The AC97 specification can be found at http://intel.com/ + + +I2S +=== + + I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and +Rx lines are used for audio transmision, whilst the bit clock (BCLK) and +left/right clock (LRC) synchronise the link. I2S is flexible in that either the +controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock +usually varies depending on the sample rate and the master system clock +(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate +ADC and DAC LRCLK's, this allows for similtanious capture and playback at +different sample rates. + +I2S has several different operating modes:- + + o I2S - MSB is transmitted on the falling edge of the first BCLK after LRC + transition. + + o Left Justified - MSB is transmitted on transition of LRC. + + o Right Justified - MSB is transmitted sample size BCLK's before LRC + transition. + +PCM +=== + +PCM is another 4 wire interface, very similar to I2S, that can support a more +flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used +to synchronise the link whilst the Tx and Rx lines are used to transmit and +receive the audio data. Bit clock usually varies depending on sample rate +whilst sync runs at the sample rate. PCM also supports Time Division +Multiplexing (TDM) in that several devices can use the bus similtaniuosly (This +is sometimes referred to as network mode). + +Common PCM operating modes:- + + o Mode A - MSB is transmitted on falling edge of first BCLK after FRAME/SYNC. + + o Mode B - MSB is transmitted on rising edge of FRAME/SYNC. + + +ASoC DAI Configuration +====================== + +Every CODEC DAI and SoC DAI must have their capabilities defined in order to +be configured together at runtime when the audio and clocking parameters are +known. This is achieved by creating an array of struct snd_soc_hw_mode in the +the CODEC and SoC interface drivers. Each element in the array describes a DAI +mode and each mode is usually based upon the DAI system clock to sample rate +ratio (FS). + +i.e. 48k sample rate @ 256 FS = sytem clock of 12.288 MHz + 48000 * 256 = 12288000 + +The CPU and Codec DAI modes are then ANDed together at runtime to determine the +rutime DAI configuration for both the Codec and CPU. + +When creating a new codec or SoC DAI it's probably best to start of with a few +sample rates first and then test your interface. + +struct snd_soc_dai_mode is defined (in soc.h) as:- + +/* SoC DAI mode */ +struct snd_soc_hw_mode { + unsigned int fmt:16; /* SND_SOC_DAIFMT_* */ + unsigned int tdm:16; /* SND_SOC_DAITDM_* */ + unsigned int pcmfmt:6; /* SNDRV_PCM_FORMAT_* */ + unsigned int pcmrate:16; /* SND_SOC_DAIRATE_* */ + unsigned int pcmdir:2; /* SND_SOC_DAIDIR_* */ + unsigned int flags:8; /* hw flags */ + unsigned int fs:32; /* mclk to rate dividers */ + unsigned int bfs:16; /* mclk to bclk dividers */ + unsigned long priv; /* private mode data */ +}; + +fmt: +---- +This field defines the DAI mode hardware format (e.g. I2S settings) and +supports the following settings:- + + 1) hardware DAI formats + +#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */ +#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */ +#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */ +#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM */ +#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM */ +#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */ + + 2) hw DAI signal inversions + +#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */ +#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */ +#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */ + + 3) hw clock masters + This is wrt the codec, the inverse is true for the interface + i.e. if the codec is clk and frm master then the interface is + clk and frame slave. + +#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */ +#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */ +#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */ +#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */ + +At least one option from each section must be selected. Multiple selections are +also supported e.g. + + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ + SND_SOC_DAIFMT_IB_IF + + +tdm: +------ +This field defines the Time Division Multiplexing left and right word +positions for the DAI mode if applicable. Set to SND_SOC_DAITDM_LRDW(0,0) for +no TDM. + + +pcmfmt: +--------- +The hardware PCM format. This describes the PCM formats supported by the DAI +mode e.g. + + .hwpcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ + SNDRV_PCM_FORMAT_S24_3LE + +pcmrate: +---------- +The PCM sample rates supported by the DAI mode. e.g. + + .hwpcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 + + +pcmdir: +--------- +The stream directions supported by this mode. e.g. playback and capture + + +flags: +-------- +The DAI hardware flags supported by the mode. + +SND_SOC_DAI_BFS_DIV +This flag states that bit clock is generated by dividing MCLK in this mode, if +this flag is absent the bitclock generated by mulitiplying sample rate. + +NOTE: Bitclock division and mulitiplication modes can be safely matched by the +core logic. + + +fs: +----- +The FS supported by this DAI mode FS is the ratio between the system clock and +the sample rate. See above + +bfs: +------ +BFS is the ratio of BCLK to MCLK or the ratio of BCLK to sample rate (this +depends on the codec or CPU DAI). + +The BFS supported by the DAI mode. This can either be the ratio between the +bitclock (BCLK) and the sample rate OR the ratio between the system clock and +the sample rate. Depends on the SND_SOC_DAI_BFS_DIV flag above. + +priv: +----- +private codec mode data. + + + +Examples +======== + +Note that Codec DAI and CPU DAI examples are interchangeable in these examples +as long as the bus master is reversed. i.e. + + SND_SOC_DAIFMT_CBM_CFM would become SND_SOC_DAIFMT_CBS_CFS + and vice versa. + +This applies to all SND_SOC_DAIFMT_CB*_CF*. + +Example 1 +--------- + +Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a +BCLK of either MCLK/2 or MCLK/4. + + /* codec master */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 256, SND_SOC_FSBD(2) | SND_SOC_FSBD(4)}, + + +Example 2 +--------- +Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a +BCLK of either Rate * 32 or Rate * 64. + + /* codec master */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, + 256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + + +Example 3 +--------- +Codec that only runs at 8k & 48k @ 256FS in master mode, can generate a +BCLK of either Rate * 32 or Rate * 64. Codec can also run in slave mode as long +as BCLK is rate * 32 or rate * 64. + + /* codec master */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, + 256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + + /* codec slave */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, + SND_SOC_FS_ALL, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + + +Example 4 +--------- +Codec that only runs at 8k, 16k, 32k, 48k, 96k @ 128FS, 192FS & 256FS in master +mode and can generate a BCLK of MCLK / (1,2,4,8,16). Codec can also run in slave +mode as and does not care about FS or BCLK (as long as there is enough bandwidth). + + #define CODEC_FSB \ + (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ + SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) + + #define CODEC_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + + /* codec master @ 128, 192 & 256 FS */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 128, CODEC_FSB}, + + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 192, CODEC_FSB}, + + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 256, CODEC_FSB}, + + /* codec slave */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, + SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, + + +Example 5 +--------- +Codec that only runs at 8k, 44.1k, 48k @ different FS in master mode (for use +with a fixed MCLK) and can generate a BCLK of MCLK / (1,2,4,8,16). +Codec can also run in slave mode as and does not care about FS or BCLK (as long +as there is enough bandwidth). Codec can support 16, 24 and 32 bit PCM sample +sizes. + + #define CODEC_FSB \ + (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ + SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) + + #define CODEC_PCM_FORMATS \ + (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ + SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE) + + /* codec master */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 1536, CODEC_FSB}, + + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_44100, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 272, CODEC_FSB}, + + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_48000, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, + 256, CODEC_FSB}, + + /* codec slave */ + {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, + SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, + SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, + + +Example 6 +--------- +AC97 Codec that does not support VRA (i.e only runs at 48k). + + #define AC97_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + + + #define AC97_PCM_FORMATS \ + (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \ + SNDRV_PCM_FORMAT_S20_3LE) + + /* AC97 with no VRA */ + {0, 0, AC97_PCM_FORMATS, SNDRV_PCM_RATE_48000}, + + +Example 7 +--------- + +CPU DAI that supports 8k - 48k @ 256FS and BCLK = MCLK / 4 in master mode. +Slave mode (CPU DAI is FRAME master) supports 8k - 96k at any FS as long as +BCLK = 64 * rate. (Intel XScale I2S controller). + + #define PXA_I2S_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF) + + #define PXA_I2S_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + + #define PXA_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + + /* pxa2xx I2S frame and clock master modes */ + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_8000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0x48}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_11025, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0x34}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_16000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0x24}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_22050, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0x1a}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_44100, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0xd}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_RATE_48000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, + SND_SOC_FSBD(4), 0xc}, + + /* pxa2xx I2S frame master and clock slave mode */ + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, + PXA_I2S_RATES, PXA_I2S_DIR, 0, SND_SOC_FS_ALL, SND_SOC_FSB(64)}, + diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/alsa/soc/clocking.txt new file mode 100644 index 000000000000..88a16c9e1979 --- /dev/null +++ b/Documentation/sound/alsa/soc/clocking.txt @@ -0,0 +1,309 @@ +Audio Clocking +============== + +This text describes the audio clocking terms in ASoC and digital audio in +general. Note: Audio clocking can be complex ! + + +Master Clock +------------ + +Every audio subsystem is driven by a master clock (sometimes refered to as MCLK +or SYSCLK). This audio master clock can be derived from a number of sources +(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct +audio playback and capture sample rates. + +Some master clocks (e.g. PLL's and CPU based clocks) are configuarble in that +their speed can be altered by software (depending on the system use and to save +power). Other master clocks are fixed at at set frequency (i.e. crystals). + + +DAI Clocks +---------- +The Digital Audio Interface is usually driven by a Bit Clock (often referred to +as BCLK). This clock is used to drive the digital audio data across the link +between the codec and CPU. + +The DAI also has a frame clock to signal the start of each audio frame. This +clock is sometimes referred to as LRC (left right clock) or FRAME. This clock +runs at exactly the sample rate. + +Bit Clock is usually always a ratio of MCLK or a multiple of LRC. i.e. + +BCLK = MCLK / x + + or + +BCLK = LRC * x + +This relationship depends on the codec or SoC CPU in particular. ASoC can quite +easily match a codec that generates BCLK by division (FSBD) with a CPU that +generates BCLK by multiplication (FSB). + + +ASoC Clocking +------------- + +The ASoC core determines the clocking for each particular configuration at +runtime. This is to allow for dynamic audio clocking wereby the audio clock is +variable and depends on the system state or device usage scenario. i.e. a voice +call requires slower clocks (and hence less power) than MP3 playback. + +ASoC will call the config_sysclock() function for the target machine during the +audio parameters configuration. The function is responsible for then clocking +the machine audio subsytem and returning the audio clock speed to the core. +This function should also call the codec and cpu DAI clock_config() functions +to configure their respective internal clocking if required. + + +ASoC Clocking Control Flow +-------------------------- + +The ASoC core will call the machine drivers config_sysclock() when most of the +DAI capabilities are known. The machine driver is then responsible for calling +the codec and/or CPU DAI drivers with the selected capabilities and the current +MCLK. Note that the machine driver is also resonsible for setting the MCLK (and +enabling it). + + (1) Match Codec and CPU DAI capabilities. At this point we have + matched the majority of the DAI fields and now need to make sure this + mode is currently clockable. + + (2) machine->config_sysclk() is now called with the matched DAI FS, sample + rate and BCLK master. This function then gets/sets the current audio + clock (depening on usage) and calls the codec and CPUI DAI drivers with + the FS, rate, BCLK master and MCLK. + + (3) Codec/CPU DAI config_sysclock(). This function checks that the FS, rate, + BCLK master and MCLK are acceptable for the codec or CPU DAI. It also + sets the DAI internal state to work with said clocks. + +The config_sysclk() functions for CPU, codec and machine should return the MCLK +on success and 0 on failure. + + +Examples (b = BCLK, l = LRC) +============================ + +Example 1 +--------- + +Simple codec that only runs at 48k @ 256FS in master mode. + +CPU only runs as slave DAI, however it generates a variable MCLK. + + -------- --------- + | | <----mclk--- | | + | Codec |b -----------> | CPU | + | |l -----------> | | + | | | | + -------- --------- + +The codec driver has the following config_sysclock() + + static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) + { + /* make sure clock is 256 * rate */ + if(info->rate << 8 == clk) { + dai->mclk = clk; + return clk; + } + + return 0; + } + +The CPU I2S DAI driver has the following config_sysclk() + + static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) + { + /* can we support this clk */ + if(set_audio_clk(clk) < 0) + return -EINVAL; + + dai->mclk = clk; + return dai->clk; + } + +The machine driver config_sysclk() in this example is as follows:- + + unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) + { + int clk = info->rate * info->fs; + + /* check that CPU can deliver clock */ + if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0) + return -EINVAL; + + /* can codec work with this clock */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk); + } + + +Example 2 +--------- + +Codec that can master at 8k and 48k at various FS (and hence supports a fixed +set of input MCLK's) and can also be slave at various FS . + +The CPU can master at 8k and 48k @256 FS and can be slave at any FS. + +MCLK is a 12.288MHz crystal on this machine. + + -------- --------- + | | <---xtal---> | | + | Codec |b <----------> | CPU | + | |l <----------> | | + | | | | + -------- --------- + + +The codec driver has the following config_sysclock() + + /* supported input clocks */ + const static int hifi_clks[] = {11289600, 12000000, 12288000, + 16934400, 18432000}; + + static unsigned int config_hsysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) + { + int i; + + /* is clk supported */ + for(i = 0; i < ARRAY_SIZE(hifi_clks); i++) { + if(clk == hifi_clks[i]) { + dai->mclk = clk; + return clk; + } + } + + /* this clk is not supported */ + return 0; + } + +The CPU I2S DAI driver has the following config_sysclk() + + static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) + { + /* are we master or slave */ + if (info->bclk_master & + (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) { + + /* we can only master @ 256FS */ + if(info->rate << 8 == clk) { + dai->mclk = clk; + return dai->mclk; + } + } else { + /* slave we can run at any FS */ + dai->mclk = clk; + return dai->mclk; + } + + /* not supported */ + return dai->clk; + } + +The machine driver config_sysclk() in this example is as follows:- + + unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) + { + int clk = 12288000; /* 12.288MHz */ + + /* who's driving the link */ + if (info->bclk_master & + (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) { + /* codec master */ + + /* check that CPU can work with clock */ + if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0) + return -EINVAL; + + /* can codec work with this clock */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk); + } else { + /* cpu master */ + + /* check that codec can work with clock */ + if(rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk) < 0) + return -EINVAL; + + /* can CPU work with this clock */ + return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk); + } + } + + + +Example 3 +--------- + +Codec that masters at 8k ... 48k @256 FS. Codec can also be slave and +doesn't care about FS. The codec has an internal PLL and dividers to generate +the necessary internal clocks (for 256FS). + +CPU can only be slave and doesn't care about FS. + +MCLK is a non controllable 13MHz clock from the CPU. + + + -------- --------- + | | <----mclk--- | | + | Codec |b <----------> | CPU | + | |l <----------> | | + | | | | + -------- --------- + +The codec driver has the following config_sysclock() + + /* valid PCM clock dividers * 2 */ + static int pcm_divs[] = {2, 6, 11, 4, 8, 12, 16}; + + static unsigned int config_vsysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) + { + int i, j, best_clk = info->fs * info->rate; + + /* can we run at this clk without the PLL ? */ + for (i = 0; i < ARRAY_SIZE(pcm_divs); i++) { + if ((best_clk >> 1) * pcm_divs[i] == clk) { + dai->pll_in = 0; + dai->clk_div = pcm_divs[i]; + dai->mclk = best_clk; + return dai->mclk; + } + } + + /* now check for PLL support */ + for (i = 0; i < ARRAY_SIZE(pll_div); i++) { + if (pll_div[i].pll_in == clk) { + for (j = 0; j < ARRAY_SIZE(pcm_divs); j++) { + if (pll_div[i].pll_out == pcm_divs[j] * (best_clk >> 1)) { + dai->pll_in = clk; + dai->pll_out = pll_div[i].pll_out; + dai->clk_div = pcm_divs[j]; + dai->mclk = best_clk; + return dai->mclk; + } + } + } + } + + /* this clk is not supported */ + return 0; + } + + +The CPU I2S DAI driver has the does not need a config_sysclk() as it can slave +at any FS. + + unsigned int config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) + { + /* codec has pll that generates mclk from 13MHz xtal */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000); + } diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt new file mode 100644 index 000000000000..47b36cb16840 --- /dev/null +++ b/Documentation/sound/alsa/soc/codec.txt @@ -0,0 +1,232 @@ +ASoC Codec Driver +================= + +The codec driver is generic and hardware independent code that configures the +codec to provide audio capture and playback. It should contain no code that is +specific to the target platform or machine. All platform and machine specific +code should be added to the platform and machine drivers respectively. + +Each codec driver must provide the following features:- + + 1) Digital audio interface (DAI) description + 2) Digital audio interface configuration + 3) PCM's description + 4) Codec control IO - using I2C, 3 Wire(SPI) or both API's + 5) Mixers and audio controls + 6) Sysclk configuration + 7) Codec audio operations + +Optionally, codec drivers can also provide:- + + 8) DAPM description. + 9) DAPM event handler. +10) DAC Digital mute control. + +It's probably best to use this guide in conjuction with the existing codec +driver code in sound/soc/codecs/ + +ASoC Codec driver breakdown +=========================== + +1 - Digital Audio Interface (DAI) description +--------------------------------------------- +The DAI is a digital audio data transfer link between the codec and host SoC +CPU. It typically has data transfer capabilities in both directions +(playback and capture) and can run at a variety of different speeds. +Supported interfaces currently include AC97, I2S and generic PCM style links. +Please read DAI.txt for implementation information. + + +2 - Digital Audio Interface (DAI) configuration +----------------------------------------------- +DAI configuration is handled by the codec_pcm_prepare function and is +responsible for configuring and starting the DAI on the codec. This can be +called multiple times and is atomic. It can access the runtime parameters. + +This usually consists of a large function with numerous switch statements to +set up each configuration option. These options are set by the core at runtime. + + +3 - Codec PCM's +--------------- +Each codec must have it's PCM's defined. This defines the number of channels, +stream names, callbacks and codec name. It is also used to register the DAI +with the ASoC core. The PCM structure also associates the DAI capabilities with +the ALSA PCM. + +e.g. + +static struct snd_soc_pcm_codec wm8731_pcm_client = { + .name = "WM8731", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + }, + .config_sysclk = wm8731_config_sysclk, + .ops = { + .prepare = wm8731_pcm_prepare, + }, + .caps = { + .num_modes = ARRAY_SIZE(wm8731_hwfmt), + .modes = &wm8731_hwfmt[0], + }, +}; + + +4 - Codec control IO +-------------------- +The codec can ususally be controlled via an I2C or SPI style interface (AC97 +combines control with data in the DAI). The codec drivers will have to provide +functions to read and write the codec registers along with supplying a register +cache:- + + /* IO control data and register cache */ + void *control_data; /* codec control (i2c/3wire) data */ + void *reg_cache; + +Codec read/write should do any data formatting and call the hardware read write +below to perform the IO. These functions are called by the core and alsa when +performing DAPM or changing the mixer:- + + unsigned int (*read)(struct snd_soc_codec *, unsigned int); + int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); + +Codec hardware IO functions - usually points to either the I2C, SPI or AC97 +read/write:- + + hw_write_t hw_write; + hw_read_t hw_read; + + +5 - Mixers and audio controls +----------------------------- +All the codec mixers and audio controls can be defined using the convenience +macros defined in soc.h. + + #define SOC_SINGLE(xname, reg, shift, mask, invert) + +Defines a single control as follows:- + + xname = Control name e.g. "Playback Volume" + reg = codec register + shift = control bit(s) offset in register + mask = control bit size(s) e.g. mask of 7 = 3 bits + invert = the control is inverted + +Other macros include:- + + #define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) + +A stereo control + + #define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) + +A stereo control spanning 2 registers + + #define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) + +Defines an single enumerated control as follows:- + + xreg = register + xshift = control bit(s) offset in register + xmask = control bit(s) size + xtexts = pointer to array of strings that describe each setting + + #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) + +Defines a stereo enumerated control + + +6 - System clock configuration. +------------------------------- +The system clock that drives the audio subsystem can change depending on sample +rate and the system power state. i.e. + +o Higher sample rates sometimes need a higher system clock. +o Low system power states can sometimes limit the available clocks. + +This function is a callback that the machine driver can call to set and +determine if the clock and sample rate combination is supported by the codec at +the present time (and system state). + +NOTE: If the codec has a PLL then it has a lot more flexability wrt clock and +sample rate combinations. + +Your config_sysclock function should return the MCLK if it's a valid +combination for your codec else 0; + +Please read clocking.txt now. + + +7 - Codec Audio Operations +-------------------------- +The codec driver also supports the following alsa operations:- + +/* SoC audio ops */ +struct snd_soc_ops { + int (*startup)(snd_pcm_substream_t *); + void (*shutdown)(snd_pcm_substream_t *); + int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *); + int (*hw_free)(snd_pcm_substream_t *); + int (*prepare)(snd_pcm_substream_t *); +}; + +Please refer to the alsa driver PCM documentation for details. +http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm + + +8 - DAPM description. +--------------------- +The Dynamic Audio Power Management description describes the codec's power +components, their relationships and registers to the ASoC core. Please read +dapm.txt for details of building the description. + +Please also see the examples in other codec drivers. + + +9 - DAPM event handler +---------------------- +This function is a callback that handles codec domain PM calls and system +domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep +when not in use. + +Power states:- + + SNDRV_CTL_POWER_D0: /* full On */ + /* vref/mid, clk and osc on, active */ + + SNDRV_CTL_POWER_D1: /* partial On */ + SNDRV_CTL_POWER_D2: /* partial On */ + + SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* everything off except vref/vmid, inactive */ + + SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */ + + +10 - Codec DAC digital mute control. +------------------------------------ +Most codecs have a digital mute before the DAC's that can be used to minimise +any system noise. The mute stops any digital data from entering the DAC. + +A callback can be created that is called by the core for each codec DAI when the +mute is applied or freed. + +i.e. + +static int wm8974_mute(struct snd_soc_codec *codec, + struct snd_soc_codec_dai *dai, int mute) +{ + u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf; + if(mute) + wm8974_write(codec, WM8974_DAC, mute_reg | 0x40); + else + wm8974_write(codec, WM8974_DAC, mute_reg); + return 0; +} diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt new file mode 100644 index 000000000000..c11877f5b4a1 --- /dev/null +++ b/Documentation/sound/alsa/soc/dapm.txt @@ -0,0 +1,297 @@ +Dynamic Audio Power Management for Portable Devices +=================================================== + +1. Description +============== + +Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices +to use the minimum amount of power within the audio subsystem at all times. It +is independent of other kernel PM and as such, can easily co-exist with the +other PM systems. + +DAPM is also completely transparent to all user space applications as all power +switching is done within the ASoC core. No code changes or recompiling are +required for user space applications. DAPM makes power switching descisions based +upon any audio stream (capture/playback) activity and audio mixer settings +within the device. + +DAPM spans the whole machine. It covers power control within the entire audio +subsystem, this includes internal codec power blocks and machine level power +systems. + +There are 4 power domains within DAPM + + 1. Codec domain - VREF, VMID (core codec and audio power) + Usually controlled at codec probe/remove and suspend/resume, although + can be set at stream time if power is not needed for sidetone, etc. + + 2. Platform/Machine domain - physically connected inputs and outputs + Is platform/machine and user action specific, is configured by the + machine driver and responds to asynchronous events e.g when HP + are inserted + + 3. Path domain - audio susbsystem signal paths + Automatically set when mixer and mux settings are changed by the user. + e.g. alsamixer, amixer. + + 4. Stream domain - DAC's and ADC's. + Enabled and disabled when stream playback/capture is started and + stopped respectively. e.g. aplay, arecord. + +All DAPM power switching descisons are made automatically by consulting an audio +routing map of the whole machine. This map is specific to each machine and +consists of the interconnections between every audio component (including +internal codec components). All audio components that effect power are called +widgets hereafter. + + +2. DAPM Widgets +=============== + +Audio DAPM widgets fall into a number of types:- + + o Mixer - Mixes several analog signals into a single analog signal. + o Mux - An analog switch that outputs only 1 of it's inputs. + o PGA - A programmable gain amplifier or attenuation widget. + o ADC - Analog to Digital Converter + o DAC - Digital to Analog Converter + o Switch - An analog switch + o Input - A codec input pin + o Output - A codec output pin + o Headphone - Headphone (and optional Jack) + o Mic - Mic (and optional Jack) + o Line - Line Input/Output (and optional Jack) + o Speaker - Speaker + o Pre - Special PRE widget (exec before all others) + o Post - Special POST widget (exec after all others) + +(Widgets are defined in include/sound/soc-dapm.h) + +Widgets are usually added in the codec driver and the machine driver. There are +convience macros defined in soc-dapm.h that can be used to quickly build a +list of widgets of the codecs and machines DAPM widgets. + +Most widgets have a name, register, shift and invert. Some widgets have extra +parameters for stream name and kcontrols. + + +2.1 Stream Domain Widgets +------------------------- + +Stream Widgets relate to the stream power domain and only consist of ADC's +(analog to digital converters) and DAC's (digital to analog converters). + +Stream widgets have the following format:- + +SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert), + +NOTE: the stream name must match the corresponding stream name in your codecs +snd_soc_codec_dai. + +e.g. stream widgets for HiFi playback and capture + +SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1), +SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1), + + +2.2 Path Domain Widgets +----------------------- + +Path domain widgets have a ability to control or effect the audio signal or +audio paths within the audio subsystem. They have the following form:- + +SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls) + +Any widget kcontrols can be set using the controls and num_controls members. + +e.g. Mixer widget (the kcontrols are declared first) + +/* Output Mixer */ +static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), +}; + +SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, + ARRAY_SIZE(wm8731_output_mixer_controls)), + + +2.3 Platform/Machine domain Widgets +----------------------------------- + +Machine widgets are different from codec widgets in that they don't have a +codec register bit associated with them. A machine widget is assigned to each +machine audio component (non codec) that can be independently powered. e.g. + + o Speaker Amp + o Microphone Bias + o Jack connectors + +A machine widget can have an optional call back. + +e.g. Jack connector widget for an external Mic that enables Mic Bias +when the Mic is inserted:- + +static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event) +{ + if(SND_SOC_DAPM_EVENT_ON(event)) + set_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS); + else + reset_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS); + + return 0; +} + +SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), + + +2.4 Codec Domain +---------------- + +The Codec power domain has no widgets and is handled by the codecs DAPM event +handler. This handler is called when the codec powerstate is changed wrt to any +stream event or by kernel PM events. + + +2.5 Virtual Widgets +------------------- + +Sometimes widgets exist in the codec or machine audio map that don't have any +corresponding register bit for power control. In this case it's necessary to +create a virtual widget - a widget with no control bits e.g. + +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0), + +This can be used to merge to signal paths together in software. + +After all the widgets have been defined, they can then be added to the DAPM +subsystem individually with a call to snd_soc_dapm_new_control(). + + +3. Codec Widget Interconnections +================================ + +Widgets are connected to each other within the codec and machine by audio +paths (called interconnections). Each interconnection must be defined in order +to create a map of all audio paths between widgets. +This is easiest with a diagram of the codec (and schematic of the machine audio +system), as it requires joining widgets together via their audio signal paths. + +i.e. from the WM8731 codec's output mixer (wm8731.c) + +The WM8731 output mixer has 3 inputs (sources) + + 1. Line Bypass Input + 2. DAC (HiFi playback) + 3. Mic Sidetone Input + +Each input in this example has a kcontrol associated with it (defined in example +above) and is connected to the output mixer via it's kcontrol name. We can now +connect the destination widget (wrt audio signal) with it's source widgets. + + /* output mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, + +So we have :- + + Destination Widget <=== Path Name <=== Source Widget + +Or:- + + Sink, Path, Source + +Or :- + + "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch". + +When there is no path name connecting widgets (e.g. a direct connection) we +pass NULL for the path name. + +Interconnections are created with a call to:- + +snd_soc_dapm_connect_input(codec, sink, path, source); + +Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and +interconnections have been registered with the core. This causes the core to +scan the codec and machine so that the internal DAPM state matches the +physical state of the machine. + + +3.1 Machine Widget Interconnections +----------------------------------- +Machine widget interconnections are created in the same way as codec ones and +directly connect the codec pins to machine level widgets. + +e.g. connects the speaker out codec pins to the internal speaker. + + /* ext speaker connected to codec pins LOUT2, ROUT2 */ + {"Ext Spk", NULL , "ROUT2"}, + {"Ext Spk", NULL , "LOUT2"}, + +This allows the DAPM to power on and off pins that are connected (and in use) +and pins that are NC respectively. + + +4 Endpoint Widgets +=================== +An endpoint is a start or end point (widget) of an audio signal within the +machine and includes the codec. e.g. + + o Headphone Jack + o Internal Speaker + o Internal Mic + o Mic Jack + o Codec Pins + +When a codec pin is NC it can be marked as not used with a call to + +snd_soc_dapm_set_endpoint(codec, "Widget Name", 0); + +The last argument is 0 for inactive and 1 for active. This way the pin and its +input widget will never be powered up and consume power. + +This also applies to machine widgets. e.g. if a headphone is connected to a +jack then the jack can be marked active. If the headphone is removed, then +the headphone jack can be marked inactive. + + +5 DAPM Widget Events +==================== + +Some widgets can register their interest with the DAPM core in PM events. +e.g. A Speaker with an amplifier registers a widget so the amplifier can be +powered only when the spk is in use. + +/* turn speaker amplifier on/off depending on use */ +static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); + else + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); + + return 0; +} + +/* corgi machine dapm widgets */ +static const struct snd_soc_dapm_widget wm8731_dapm_widgets = + SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event); + +Please see soc-dapm.h for all other widgets that support events. + + +5.1 Event types +--------------- + +The following event types are supported by event widgets. + +/* dapm event types */ +#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */ +#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */ +#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */ +#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ +#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ +#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt new file mode 100644 index 000000000000..3014795b1733 --- /dev/null +++ b/Documentation/sound/alsa/soc/machine.txt @@ -0,0 +1,114 @@ +ASoC Machine Driver +=================== + +The ASoC machine (or board) driver is the code that glues together the platform +and codec drivers. + +The machine driver can contain codec and platform specific code. It registers +the audio subsystem with the kernel as a platform device and is represented by +the following struct:- + +/* SoC machine */ +struct snd_soc_machine { + char *name; + + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); + + /* the pre and post PM functions are used to do any PM work before and + * after the codec and DAI's do any PM work. */ + int (*suspend_pre)(struct platform_device *pdev, pm_message_t state); + int (*suspend_post)(struct platform_device *pdev, pm_message_t state); + int (*resume_pre)(struct platform_device *pdev); + int (*resume_post)(struct platform_device *pdev); + + /* machine stream operations */ + struct snd_soc_ops *ops; + + /* CPU <--> Codec DAI links */ + struct snd_soc_dai_link *dai_link; + int num_links; +}; + +probe()/remove() +---------------- +probe/remove are optional. Do any machine specific probe here. + + +suspend()/resume() +------------------ +The machine driver has pre and post versions of suspend and resume to take care +of any machine audio tasks that have to be done before or after the codec, DAI's +and DMA is suspended and resumed. Optional. + + +Machine operations +------------------ +The machine specific audio operations can be set here. Again this is optional. + + +Machine DAI Configuration +------------------------- +The machine DAI configuration glues all the codec and CPU DAI's together. It can +also be used to set up the DAI system clock and for any machine related DAI +initialisation e.g. the machine audio map can be connected to the codec audio +map, unconnnected codec pins can be set as such. Please see corgi.c, spitz.c +for examples. + +struct snd_soc_dai_link is used to set up each DAI in your machine. e.g. + +/* corgi digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link corgi_dai = { + .name = "WM8731", + .stream_name = "WM8731", + .cpu_dai = &pxa_i2s_dai, + .codec_dai = &wm8731_dai, + .init = corgi_wm8731_init, + .config_sysclk = corgi_config_sysclk, +}; + +struct snd_soc_machine then sets up the machine with it's DAI's. e.g. + +/* corgi audio machine driver */ +static struct snd_soc_machine snd_soc_machine_corgi = { + .name = "Corgi", + .dai_link = &corgi_dai, + .num_links = 1, + .ops = &corgi_ops, +}; + + +Machine Audio Subsystem +----------------------- + +The machine soc device glues the platform, machine and codec driver together. +Private data can also be set here. e.g. + +/* corgi audio private data */ +static struct wm8731_setup_data corgi_wm8731_setup = { + .i2c_address = 0x1b, +}; + +/* corgi audio subsystem */ +static struct snd_soc_device corgi_snd_devdata = { + .machine = &snd_soc_machine_corgi, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8731, + .codec_data = &corgi_wm8731_setup, +}; + + +Machine Power Map +----------------- + +The machine driver can optionally extend the codec power map and to become an +audio power map of the audio subsystem. This allows for automatic power up/down +of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack +sockets in the machine init function. See soc/pxa/spitz.c and dapm.txt for +details. + + +Machine Controls +---------------- + +Machine specific audio mixer controls can be added in the dai init function. \ No newline at end of file diff --git a/Documentation/sound/alsa/soc/overview.txt b/Documentation/sound/alsa/soc/overview.txt new file mode 100644 index 000000000000..753c5cc5984a --- /dev/null +++ b/Documentation/sound/alsa/soc/overview.txt @@ -0,0 +1,83 @@ +ALSA SoC Layer +============== + +The overall project goal of the ALSA System on Chip (ASoC) layer is to provide +better ALSA support for embedded system on chip procesors (e.g. pxa2xx, au1x00, +iMX, etc) and portable audio codecs. Currently there is some support in the +kernel for SoC audio, however it has some limitations:- + + * Currently, codec drivers are often tightly coupled to the underlying SoC + cpu. This is not ideal and leads to code duplication i.e. Linux now has 4 + different wm8731 drivers for 4 different SoC platforms. + + * There is no standard method to signal user initiated audio events. + e.g. Headphone/Mic insertion, Headphone/Mic detection after an insertion + event. These are quite common events on portable devices and ofter require + machine specific code to re route audio, enable amps etc after such an event. + + * Current drivers tend to power up the entire codec when playing + (or recording) audio. This is fine for a PC, but tends to waste a lot of + power on portable devices. There is also no support for saving power via + changing codec oversampling rates, bias currents, etc. + + +ASoC Design +=========== + +The ASoC layer is designed to address these issues and provide the following +features :- + + * Codec independence. Allows reuse of codec drivers on other platforms + and machines. + + * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface + and codec registers it's audio interface capabilities with the core and are + subsequently matched and configured when the application hw params are known. + + * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to + it's minimum power state at all times. This includes powering up/down + internal power blocks depending on the internal codec audio routing and any + active streams. + + * Pop and click reduction. Pops and clicks can be reduced by powering the + codec up/down in the correct sequence (including using digital mute). ASoC + signals the codec when to change power states. + + * Machine specific controls: Allow machines to add controls to the sound card + e.g. volume control for speaker amp. + +To achieve all this, ASoC basically splits an embedded audio system into 3 +components :- + + * Codec driver: The codec driver is platform independent and contains audio + controls, audio interface capabilities, codec dapm definition and codec IO + functions. + + * Platform driver: The platform driver contains the audio dma engine and audio + interface drivers (e.g. I2S, AC97, PCM) for that platform. + + * Machine driver: The machine driver handles any machine specific controls and + audio events. i.e. turing on an amp at start of playback. + + +Documentation +============= + +The documentation is spilt into the following sections:- + +overview.txt: This file. + +codec.txt: Codec driver internals. + +DAI.txt: Description of Digital Audio Interface standards and how to configure +a DAI within your codec and CPU DAI drivers. + +dapm.txt: Dynamic Audio Power Management + +platform.txt: Platform audio DMA and DAI. + +machine.txt: Machine driver internals. + +pop_clicks.txt: How to minimise audio artifacts. + +clocking.txt: ASoC clocking for best power performance. \ No newline at end of file diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt new file mode 100644 index 000000000000..c88df261e922 --- /dev/null +++ b/Documentation/sound/alsa/soc/platform.txt @@ -0,0 +1,58 @@ +ASoC Platform Driver +==================== + +An ASoC platform driver can be divided into audio DMA and SoC DAI configuration +and control. The platform drivers only target the SoC CPU and must have no board +specific code. + +Audio DMA +========= + +The platform DMA driver optionally supports the following alsa operations:- + +/* SoC audio ops */ +struct snd_soc_ops { + int (*startup)(snd_pcm_substream_t *); + void (*shutdown)(snd_pcm_substream_t *); + int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *); + int (*hw_free)(snd_pcm_substream_t *); + int (*prepare)(snd_pcm_substream_t *); + int (*trigger)(snd_pcm_substream_t *, int); +}; + +The platform driver exports it's DMA functionailty via struct snd_soc_platform:- + +struct snd_soc_platform { + char *name; + + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); + int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); + int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); + + /* pcm creation and destruction */ + int (*pcm_new)(snd_card_t *, struct snd_soc_codec_dai *, snd_pcm_t *); + void (*pcm_free)(snd_pcm_t *); + + /* platform stream ops */ + snd_pcm_ops_t *pcm_ops; +}; + +Please refer to the alsa driver documentation for details of audio DMA. +http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm + +An example DMA driver is soc/pxa/pxa2xx-pcm.c + + +SoC DAI Drivers +=============== + +Each SoC DAI driver must provide the following features:- + + 1) Digital audio interface (DAI) description + 2) Digital audio interface configuration + 3) PCM's description + 4) Sysclk configuration + 5) Suspend and resume (optional) + +Please see codec.txt for a description of items 1 - 4. diff --git a/Documentation/sound/alsa/soc/pops_clicks.txt b/Documentation/sound/alsa/soc/pops_clicks.txt new file mode 100644 index 000000000000..f4f8de5a9686 --- /dev/null +++ b/Documentation/sound/alsa/soc/pops_clicks.txt @@ -0,0 +1,52 @@ +Audio Pops and Clicks +===================== + +Pops and clicks are unwanted audio artifacts caused by the powering up and down +of components within the audio subsystem. This is noticable on PC's when an audio +module is either loaded or unloaded (at module load time the sound card is +powered up and causes a popping noise on the speakers). + +Pops and clicks can be more frequent on portable systems with DAPM. This is because +the components within the subsystem are being dynamically powered depending on +the audio usage and this can subsequently cause a small pop or click every time a +component power state is changed. + + +Minimising Playback Pops and Clicks +=================================== + +Playback pops in portable audio subsystems cannot be completely eliminated atm, +however future audio codec hardware will have better pop and click supression. +Pops can be reduced within playback by powering the audio components in a +specific order. This order is different for startup and shutdown and follows +some basic rules:- + + Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute + + Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC + +This assumes that the codec PCM output path from the DAC is via a mixer and then +a PGA (programmable gain amplifier) before being output to the speakers. + + +Minimising Capture Pops and Clicks +================================== + +Capture artifacts are somewhat easier to get rid as we can delay activating the +ADC until all the pops have occured. This follows similar power rules to +playback in that components are powered in a sequence depending upon stream +startup or shutdown. + + Startup Order - Input PGA --> Mixers --> ADC + + Shutdown Order - ADC --> Mixers --> Input PGA + + +Zipper Noise +============ +An unwanted zipper noise can occur within the audio playback or capture stream +when a volume control is changed near its maximum gain value. The zipper noise +is heard when the gain increase or decrease changes the mean audio signal +amplitude too quickly. It can be minimised by enabling the zero cross setting +for each volume control. The ZC forces the gain change to occur when the signal +crosses the zero amplitude line. diff --git a/MAINTAINERS b/MAINTAINERS index fe35f3ac4cd3..010658a8b5a0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3037,6 +3037,12 @@ M: perex@suse.cz L: alsa-devel@alsa-project.org S: Maintained +SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT +P: Liam Girdwood +M: liam.girdwood@wolfsonmicro.com +L: alsa-devel@alsa-project.org +S: Supported + SPI SUBSYSTEM P: David Brownell M: dbrownell@users.sourceforge.net -- cgit v1.2.3-59-g8ed1b From 40e0aa64660b4e28a9348e57bfbda6c114617969 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:36:07 +0200 Subject: [ALSA] ASoC codecs: WM8731 support This patch adds ASoC support for the WM8731 codec. Supported features:- o Capture/Playback/Sidetone/Bypass. o 16 & 24 bit audio. o 8k - 96k sample rates. o DAPM. Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/linux/i2c-id.h | 1 + sound/soc/codecs/wm8731.c | 789 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8731.h | 41 +++ 3 files changed, 831 insertions(+) create mode 100644 sound/soc/codecs/wm8731.c create mode 100644 sound/soc/codecs/wm8731.h diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index d38778f2fbec..01e98c2a9618 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -115,6 +115,7 @@ #define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ #define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */ +#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */ #define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c new file mode 100644 index 000000000000..cd0ece650f31 --- /dev/null +++ b/sound/soc/codecs/wm8731.c @@ -0,0 +1,789 @@ +/* + * wm8731.c -- WM8731 ALSA SoC Audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on wm8753.c by Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8731.h" + +#define AUDIO_NAME "wm8731" +#define WM8731_VERSION "0.12" + +/* + * Debug + */ + +#define WM8731_DEBUG 0 + +#ifdef WM8731_DEBUG +#define dbg(format, arg...) \ + printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) \ + printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) \ + printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) + +struct snd_soc_codec_device soc_codec_dev_wm8731; + +/* + * wm8731 register cache + * We can't read the WM8731 register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { + 0x0097, 0x0097, 0x0079, 0x0079, + 0x000a, 0x0008, 0x009f, 0x000a, + 0x0000, 0x0000 +}; + +#define WM8731_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ + SND_SOC_DAIFMT_IB_IF) + +#define WM8731_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define WM8731_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8731_HIFI_BITS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_mode wm8731_modes[] = { + /* codec frame and clock master modes */ + /* 8k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, + 1536, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, + 2304, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, + 1408, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, + 2112, SND_SOC_FSB(64)}, + + /* 32k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_32000, WM8731_DIR, 0, + 384, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_32000, WM8731_DIR, 0, + 576, SND_SOC_FSB(64)}, + + /* 44.1k & 48k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + WM8731_DIR, 0, 256, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + WM8731_DIR, 0, 384, SND_SOC_FSB(64)}, + + /* 88.2 & 96k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + WM8731_DIR, 0, 128, SND_SOC_FSB(64)}, + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + WM8731_DIR, 0, 192, SND_SOC_FSB(64)}, + + + /* USB codec frame and clock master modes */ + /* 8k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, + SND_SOC_DAI_BFS_DIV, 1500, SND_SOC_FSBD(1)}, + + /* 44.1k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100, WM8731_DIR, + SND_SOC_DAI_BFS_DIV, 272, SND_SOC_FSBD(1)}, + + /* 48k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_48000, WM8731_DIR, + SND_SOC_DAI_BFS_DIV, 250, SND_SOC_FSBD(1)}, + + /* 88.2k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200, WM8731_DIR, + SND_SOC_DAI_BFS_DIV, 136, SND_SOC_FSBD(1)}, + + /* 96k */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, SNDRV_PCM_RATE_96000, WM8731_DIR, + SND_SOC_DAI_BFS_DIV, 125, SND_SOC_FSBD(1)}, + + /* codec frame and clock slave modes */ + {WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + WM8731_HIFI_BITS, WM8731_RATES, WM8731_DIR, SND_SOC_DAI_BFS_DIV, + SND_SOC_FS_ALL, SND_SOC_FSBD_ALL}, +}; + +/* + * read wm8731 register cache + */ +static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg == WM8731_RESET) + return 0; + if (reg >= WM8731_CACHEREGNUM) + return -1; + return cache[reg]; +} + +/* + * write wm8731 register cache + */ +static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg >= WM8731_CACHEREGNUM) + return; + cache[reg] = value; +} + +/* + * write to the WM8731 register space + */ +static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + /* data is + * D15..D9 WM8731 register offset + * D8...D0 register data + */ + data[0] = (reg << 1) | ((value >> 8) & 0x0001); + data[1] = value & 0x00ff; + + wm8731_write_reg_cache (codec, reg, value); + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0) + +static const char *wm8731_input_select[] = {"Line In", "Mic"}; +static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static const struct soc_enum wm8731_enum[] = { + SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), + SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), +}; + +static const struct snd_kcontrol_new wm8731_snd_controls[] = { + +SOC_DOUBLE_R("Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, 0, 127, 0), +SOC_DOUBLE_R("Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 7, 1, 0), + +SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0), +SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), + +SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), +SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1), + +SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1), + +SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), + +SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), +}; + +/* add non dapm controls */ +static int wm8731_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) { + if ((err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0) + return err; + } + + return 0; +} + +/* Output Mixer */ +static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), +}; + +/* Input mux */ +static const struct snd_kcontrol_new wm8731_input_mux_controls = +SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); + +static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, + &wm8731_output_mixer_controls[0], + ARRAY_SIZE(wm8731_output_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), +SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), +SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static const char *intercon[][3] = { + /* output mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, + + /* outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + + /* input mux */ + {"Input Mux", "Line In", "Line Input"}, + {"Input Mux", "Mic", "Mic Bias"}, + {"ADC", NULL, "Input Mux"}, + + /* inputs */ + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, + {"Mic Bias", NULL, "MICIN"}, + + /* terminator */ + {NULL, NULL, NULL}, +}; + +static int wm8731_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); + } + + /* set up audio path interconnects */ + for(i = 0; intercon[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, intercon[i][0], + intercon[i][1], intercon[i][2]); + } + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:4; + u8 bosr:1; + u8 usb:1; +}; + +/* codec mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0, 0x0}, + {18432000, 48000, 384, 0x0, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x0, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0x6, 0x0, 0x0}, + {18432000, 32000, 576, 0x6, 0x1, 0x0}, + + /* 8k */ + {12288000, 8000, 1536, 0x3, 0x0, 0x0}, + {18432000, 8000, 2304, 0x3, 0x1, 0x0}, + {11289600, 8000, 1408, 0xb, 0x0, 0x0}, + {16934400, 8000, 2112, 0xb, 0x1, 0x0}, + {12000000, 8000, 1500, 0x3, 0x0, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0x7, 0x0, 0x0}, + {18432000, 96000, 192, 0x7, 0x1, 0x0}, + {12000000, 96000, 125, 0x7, 0x0, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x8, 0x0, 0x0}, + {16934400, 44100, 384, 0x8, 0x1, 0x0}, + {12000000, 44100, 272, 0x8, 0x1, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0xf, 0x0, 0x0}, + {16934400, 88200, 192, 0xf, 0x1, 0x0}, + {12000000, 88200, 136, 0xf, 0x1, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return 0; +} + +/* WM8731 supports numerous clocks per sample rate */ +static unsigned int wm8731_config_sysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) +{ + dai->mclk = 0; + + /* check that the calculated FS and rate actually match a clock from + * the machine driver */ + if (info->fs * info->rate == clk) + dai->mclk = clk; + + return dai->mclk; +} + +static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 iface = 0, srate; + int i = get_coeff(rtd->codec_dai->mclk, + snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate)); + + /* set master/slave audio interface */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + } + srate = (coeff_div[i].sr << 2) | + (coeff_div[i].bosr << 1) | coeff_div[i].usb; + wm8731_write(codec, WM8731_SRATE, srate); + + /* interface format */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + } + + /* bit size */ + switch (rtd->codec_dai->dai_runtime.pcmfmt) { + case SNDRV_PCM_FMTBIT_S16_LE: + break; + case SNDRV_PCM_FMTBIT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FMTBIT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FMTBIT_S32_LE: + iface |= 0x000c; + break; + } + + /* clock inversion */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + } + + /* set iface */ + wm8731_write(codec, WM8731_IFACE, iface); + + /* set active */ + wm8731_write(codec, WM8731_ACTIVE, 0x0001); + return 0; +} + +static void wm8731_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + + /* deactivate */ + if (!codec->active) { + udelay(50); + wm8731_write(codec, WM8731_ACTIVE, 0x0); + } +} + +static int wm8731_mute(struct snd_soc_codec *codec, + struct snd_soc_codec_dai *dai, int mute) +{ + u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7; + if (mute) + wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8); + else + wm8731_write(codec, WM8731_APDIGI, mute_reg); + return 0; +} + +static int wm8731_dapm_event(struct snd_soc_codec *codec, int event) +{ + u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f; + + switch (event) { + case SNDRV_CTL_POWER_D0: /* full On */ + /* vref/mid, osc on, dac unmute */ + wm8731_write(codec, WM8731_PWR, reg); + break; + case SNDRV_CTL_POWER_D1: /* partial On */ + case SNDRV_CTL_POWER_D2: /* partial On */ + break; + case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* everything off except vref/vmid, */ + wm8731_write(codec, WM8731_PWR, reg | 0x0040); + break; + case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + /* everything off, dac mute, inactive */ + wm8731_write(codec, WM8731_ACTIVE, 0x0); + wm8731_write(codec, WM8731_PWR, 0xffff); + break; + } + codec->dapm_state = event; + return 0; +} + +struct snd_soc_codec_dai wm8731_dai = { + .name = "WM8731", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + }, + .config_sysclk = wm8731_config_sysclk, + .digital_mute = wm8731_mute, + .ops = { + .prepare = wm8731_pcm_prepare, + .shutdown = wm8731_shutdown, + }, + .caps = { + .num_modes = ARRAY_SIZE(wm8731_modes), + .mode = wm8731_modes, + }, +}; +EXPORT_SYMBOL_GPL(wm8731_dai); + +static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + wm8731_write(codec, WM8731_ACTIVE, 0x0); + wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + return 0; +} + +static int wm8731_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i; + u8 data[2]; + u16 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { + data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); + data[1] = cache[i] & 0x00ff; + codec->hw_write(codec->control_data, data, 2); + } + wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm8731_dapm_event(codec, codec->suspend_dapm_state); + return 0; +} + +/* + * initialise the WM8731 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8731_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + int reg, ret = 0; + + codec->name = "WM8731"; + codec->owner = THIS_MODULE; + codec->read = wm8731_read_reg_cache; + codec->write = wm8731_write; + codec->dapm_event = wm8731_dapm_event; + codec->dai = &wm8731_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(wm8731_reg); + + codec->reg_cache = + kzalloc(sizeof(u16) * ARRAY_SIZE(wm8731_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + memcpy(codec->reg_cache, + wm8731_reg, sizeof(u16) * ARRAY_SIZE(wm8731_reg)); + codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8731_reg); + + wm8731_reset(codec); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + kfree(codec->reg_cache); + return ret; + } + + /* power on device */ + wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* set the update bits */ + reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); + wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100); + reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); + wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100); + reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); + wm8731_write(codec, WM8731_LINVOL, reg | 0x0100); + reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); + wm8731_write(codec, WM8731_RINVOL, reg | 0x0100); + + wm8731_add_controls(codec); + wm8731_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) { + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + } + + return ret; +} + +static struct snd_soc_device *wm8731_socdev; + +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + +/* + * WM8731 2 wire address is determined by GPIO5 + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver wm8731_i2c_driver; +static struct i2c_client client_template; + +/* If the i2c layer weren't so broken, we could pass this kind of data + around */ + +static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ + struct snd_soc_device *socdev = wm8731_socdev; + struct wm8731_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec = socdev->codec; + struct i2c_client *i2c; + int ret; + + if (addr != setup->i2c_address) + return -ENODEV; + + client_template.adapter = adap; + client_template.addr = addr; + + i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (i2c == NULL) { + kfree(codec); + return -ENOMEM; + } + memcpy(i2c, &client_template, sizeof(struct i2c_client)); + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = i2c_attach_client(i2c); + if (ret < 0) { + err("failed to attach codec at addr %x\n", addr); + goto err; + } + + ret = wm8731_init(socdev); + if (ret < 0) { + err("failed to initialise WM8731\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} + +static int wm8731_i2c_detach(struct i2c_client *client) +{ + struct snd_soc_codec* codec = i2c_get_clientdata(client); + i2c_detach_client(client); + kfree(codec->reg_cache); + kfree(client); + return 0; +} + +static int wm8731_i2c_attach(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, wm8731_codec_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver wm8731_i2c_driver = { + .driver = { + .name = "WM8731 I2C Codec", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_WM8731, + .attach_adapter = wm8731_i2c_attach, + .detach_client = wm8731_i2c_detach, + .command = NULL, +}; + +static struct i2c_client client_template = { + .name = "WM8731", + .driver = &wm8731_i2c_driver, +}; +#endif + +static int wm8731_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct wm8731_setup_data *setup; + struct snd_soc_codec *codec; + int ret = 0; + + info("WM8731 Audio Codec %s", WM8731_VERSION); + + setup = socdev->codec_data; + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + wm8731_socdev = socdev; +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + if (setup->i2c_address) { + normal_i2c[0] = setup->i2c_address; + codec->hw_write = (hw_write_t)i2c_master_send; + ret = i2c_add_driver(&wm8731_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); + } +#else + /* Add other interfaces here */ +#endif + return ret; +} + +/* power down chip */ +static int wm8731_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec->control_data) + wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + i2c_del_driver(&wm8731_i2c_driver); +#endif + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8731 = { + .probe = wm8731_probe, + .remove = wm8731_remove, + .suspend = wm8731_suspend, + .resume = wm8731_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); + +MODULE_DESCRIPTION("ASoC WM8731 driver"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h new file mode 100644 index 000000000000..8fa0f53bef1c --- /dev/null +++ b/sound/soc/codecs/wm8731.h @@ -0,0 +1,41 @@ +/* + * wm8731.h -- WM8731 Soc Audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on wm8753.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8731_H +#define _WM8731_H + +/* WM8731 register space */ + +#define WM8731_LINVOL 0x00 +#define WM8731_RINVOL 0x01 +#define WM8731_LOUT1V 0x02 +#define WM8731_ROUT1V 0x03 +#define WM8731_APANA 0x04 +#define WM8731_APDIGI 0x05 +#define WM8731_PWR 0x06 +#define WM8731_IFACE 0x07 +#define WM8731_SRATE 0x08 +#define WM8731_ACTIVE 0x09 +#define WM8731_RESET 0x0f + +#define WM8731_CACHEREGNUM 10 + +struct wm8731_setup_data { + unsigned short i2c_address; +}; + +extern struct snd_soc_codec_dai wm8731_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8731; + +#endif -- cgit v1.2.3-59-g8ed1b From abadfc928a27e1cf27c834e8e29e6b1f64ca2d55 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:36:39 +0200 Subject: [ALSA] ASoC codecs: WM8750 support This patch adds ASoC support for the WM8750 codec. Supported features:- o Capture/Playback/Sidetone/Bypass. o 16 & 24 bit audio. o 8k - 96k sample rates. o DAPM. Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/linux/i2c-id.h | 1 + sound/soc/codecs/wm8750.c | 1131 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8750.h | 66 +++ 3 files changed, 1198 insertions(+) create mode 100644 sound/soc/codecs/wm8750.c create mode 100644 sound/soc/codecs/wm8750.h diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 01e98c2a9618..6e7ec4c76178 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -116,6 +116,7 @@ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ #define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */ #define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */ +#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */ #define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c new file mode 100644 index 000000000000..6a8b2799b3b1 --- /dev/null +++ b/sound/soc/codecs/wm8750.c @@ -0,0 +1,1131 @@ +/* + * wm8750.c -- WM8750 ALSA SoC audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on WM8753.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8750.h" + +#define AUDIO_NAME "WM8750" +#define WM8750_VERSION "0.11" + +/* + * Debug + */ + +#define WM8750_DEBUG 0 + +#ifdef WM8750_DEBUG +#define dbg(format, arg...) \ + printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) \ + printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) \ + printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) + +static struct workqueue_struct *wm8750_workq = NULL; +static struct work_struct wm8750_dapm_work; + +/* + * wm8750 register cache + * We can't read the WM8750 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 wm8750_reg[] = { + 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ + 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ + 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ + 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ + 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ + 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ + 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ + 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ + 0x0079, 0x0079, 0x0079, /* 40 */ +}; + +#define WM8750_HIFI_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ + SND_SOC_DAIFMT_IB_IF) + +#define WM8750_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define WM8750_HIFI_FSB \ + (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ + SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) + +#define WM8750_HIFI_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8750_HIFI_BITS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_mode wm8750_modes[] = { + /* common codec frame and clock master modes */ + /* 8k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1536, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1408, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 2304, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 2112, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1500, WM8750_HIFI_FSB}, + + /* 11.025k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1024, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1536, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1088, WM8750_HIFI_FSB}, + + /* 16k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 768, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1152, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 750, WM8750_HIFI_FSB}, + + /* 22.05k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 512, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 768, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 544, WM8750_HIFI_FSB}, + + /* 32k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 384, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 576, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 375, WM8750_HIFI_FSB}, + + /* 44.1k & 48k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 256, + WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 384, + WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 272, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_48000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 250, WM8750_HIFI_FSB}, + + /* 88.2k & 96k */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 128, + WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 192, + WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 136, WM8750_HIFI_FSB}, + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_96000, + WM8750_DIR, SND_SOC_DAI_BFS_DIV, 125, WM8750_HIFI_FSB}, + + /* codec frame and clock slave modes */ + {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + WM8750_HIFI_BITS, WM8750_HIFI_RATES, WM8750_DIR, + SND_SOC_DAI_BFS_DIV, SND_SOC_FS_ALL, SND_SOC_FSBD_ALL}, +}; + +/* + * read wm8750 register cache + */ +static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg > WM8750_CACHE_REGNUM) + return -1; + return cache[reg]; +} + +/* + * write wm8750 register cache + */ +static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg > WM8750_CACHE_REGNUM) + return; + cache[reg] = value; +} + +static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + /* data is + * D15..D9 WM8753 register offset + * D8...D0 register data + */ + data[0] = (reg << 1) | ((value >> 8) & 0x0001); + data[1] = value & 0x00ff; + + wm8750_write_reg_cache (codec, reg, value); + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0) + +/* + * WM8750 Controls + */ +static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" }; +static const char *wm8750_treble[] = {"8kHz", "4kHz"}; +static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"}; +static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"}; +static const char *wm8750_3d_func[] = {"Capture", "Playback"}; +static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"}; +static const char *wm8750_ng_type[] = {"Constant PGA Gain", + "Mute ADC Output"}; +static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA", + "Differential"}; +static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3", + "Differential"}; +static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut", + "ROUT1"}; +static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"}; +static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; +static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; + +static const struct soc_enum wm8750_enum[] = { +SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass), +SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter), +SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble), +SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc), +SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc), +SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func), +SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func), +SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type), +SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux), +SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux), +SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */ +SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel), +SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3), +SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel), +SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol), +SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph), +SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */ + +}; + +static const struct snd_kcontrol_new wm8750_snd_controls[] = { + +SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0), +SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1), + +SOC_DOUBLE_R("Out1 Playback ZC Switch", WM8750_LOUT1V, + WM8750_ROUT1V, 7, 1, 0), +SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8750_LOUT2V, + WM8750_ROUT2V, 7, 1, 0), + +SOC_ENUM("Playback De-emphasis", wm8750_enum[15]), + +SOC_ENUM("Capture Polarity", wm8750_enum[14]), +SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0), +SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0), + +SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0), + +SOC_ENUM("Bass Boost", wm8750_enum[0]), +SOC_ENUM("Bass Filter", wm8750_enum[1]), +SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), + +SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0), +SOC_ENUM("Treble Cut-off", wm8750_enum[2]), + +SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), +SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0), +SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]), +SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]), +SOC_ENUM("3D Mode", wm8750_enum[5]), + +SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0), +SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0), +SOC_ENUM("ALC Capture Function", wm8750_enum[6]), +SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0), +SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0), +SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0), +SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0), +SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]), +SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0), + +SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0), +SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0), + +SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0), +SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0), + +SOC_SINGLE("Right Out2 Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0), + +/* Unimplemented */ +/* ADCDAC Bit 0 - ADCHPD */ +/* ADCDAC Bit 4 - HPOR */ +/* ADCTL1 Bit 2,3 - DATSEL */ +/* ADCTL1 Bit 4,5 - DMONOMIX */ +/* ADCTL1 Bit 6,7 - VSEL */ +/* ADCTL2 Bit 2 - LRCM */ +/* ADCTL2 Bit 3 - TRI */ +/* ADCTL3 Bit 5 - HPFLREN */ +/* ADCTL3 Bit 6 - VROI */ +/* ADCTL3 Bit 7,8 - ADCLRM */ +/* ADCIN Bit 4 - LDCM */ +/* ADCIN Bit 5 - RDCM */ + +SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0), + +SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1, + WM8750_LOUTM2, 4, 7, 1), +SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1, + WM8750_ROUTM2, 4, 7, 1), +SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1, + WM8750_MOUTM2, 4, 7, 1), + +SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0), + +SOC_DOUBLE_R("Out1 Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V, 0, 127, 0), +SOC_DOUBLE_R("Out2 Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V, 0, 127, 0), + +SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0), + +}; + +/* add non dapm controls */ +static int wm8750_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +/* + * DAPM Controls + */ + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0), +}; + +/* Left Line Mux */ +static const struct snd_kcontrol_new wm8750_left_line_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[8]); + +/* Right Line Mux */ +static const struct snd_kcontrol_new wm8750_right_line_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[9]); + +/* Left PGA Mux */ +static const struct snd_kcontrol_new wm8750_left_pga_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[10]); + +/* Right PGA Mux */ +static const struct snd_kcontrol_new wm8750_right_pga_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[11]); + +/* Out 3 Mux */ +static const struct snd_kcontrol_new wm8750_out3_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[12]); + +/* Differential Mux */ +static const struct snd_kcontrol_new wm8750_diffmux_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[13]); + +/* Mono ADC Mux */ +static const struct snd_kcontrol_new wm8750_monomux_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[16]); + +static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &wm8750_left_mixer_controls[0], + ARRAY_SIZE(wm8750_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &wm8750_right_mixer_controls[0], + ARRAY_SIZE(wm8750_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0, + &wm8750_mono_mixer_controls[0], + ARRAY_SIZE(wm8750_mono_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0), + + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0), + + SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0, + &wm8750_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0, + &wm8750_right_pga_controls), + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &wm8750_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &wm8750_right_line_controls), + + SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls), + SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &wm8750_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8750_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8750_monomux_controls), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("MONO"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("LINPUT3"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT3"), +}; + +static const char *audio_map[][3] = { + /* left mixer */ + {"Left Mixer", "Playback Switch", "Left DAC"}, + {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Left Mixer", "Right Playback Switch", "Right DAC"}, + {"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* right mixer */ + {"Right Mixer", "Left Playback Switch", "Left DAC"}, + {"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Right Mixer", "Playback Switch", "Right DAC"}, + {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* left out 1 */ + {"Left Out 1", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + + /* left out 2 */ + {"Left Out 2", NULL, "Left Mixer"}, + {"LOUT2", NULL, "Left Out 2"}, + + /* right out 1 */ + {"Right Out 1", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + + /* right out 2 */ + {"Right Out 2", NULL, "Right Mixer"}, + {"ROUT2", NULL, "Right Out 2"}, + + /* mono mixer */ + {"Mono Mixer", "Left Playback Switch", "Left DAC"}, + {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Mono Mixer", "Right Playback Switch", "Right DAC"}, + {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* mono out */ + {"Mono Out 1", NULL, "Mono Mixer"}, + {"MONO1", NULL, "Mono Out 1"}, + + /* out 3 */ + {"Out3 Mux", "VREF", "VREF"}, + {"Out3 Mux", "ROUT1 + Vol", "ROUT1"}, + {"Out3 Mux", "ROUT1", "Right Mixer"}, + {"Out3 Mux", "MonoOut", "MONO1"}, + {"Out 3", NULL, "Out3 Mux"}, + {"OUT3", NULL, "Out 3"}, + + /* Left Line Mux */ + {"Left Line Mux", "Line 1", "LINPUT1"}, + {"Left Line Mux", "Line 2", "LINPUT2"}, + {"Left Line Mux", "Line 3", "LINPUT3"}, + {"Left Line Mux", "PGA", "Left PGA Mux"}, + {"Left Line Mux", "Differential", "Differential Mux"}, + + /* Right Line Mux */ + {"Right Line Mux", "Line 1", "RINPUT1"}, + {"Right Line Mux", "Line 2", "RINPUT2"}, + {"Right Line Mux", "Line 3", "RINPUT3"}, + {"Right Line Mux", "PGA", "Right PGA Mux"}, + {"Right Line Mux", "Differential", "Differential Mux"}, + + /* Left PGA Mux */ + {"Left PGA Mux", "Line 1", "LINPUT1"}, + {"Left PGA Mux", "Line 2", "LINPUT2"}, + {"Left PGA Mux", "Line 3", "LINPUT3"}, + {"Left PGA Mux", "Differential", "Differential Mux"}, + + /* Right PGA Mux */ + {"Right PGA Mux", "Line 1", "RINPUT1"}, + {"Right PGA Mux", "Line 2", "RINPUT2"}, + {"Right PGA Mux", "Line 3", "RINPUT3"}, + {"Right PGA Mux", "Differential", "Differential Mux"}, + + /* Differential Mux */ + {"Differential Mux", "Line 1", "LINPUT1"}, + {"Differential Mux", "Line 1", "RINPUT1"}, + {"Differential Mux", "Line 2", "LINPUT2"}, + {"Differential Mux", "Line 2", "RINPUT2"}, + + /* Left ADC Mux */ + {"Left ADC Mux", "Stereo", "Left PGA Mux"}, + {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, + {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + + /* Right ADC Mux */ + {"Right ADC Mux", "Stereo", "Right PGA Mux"}, + {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, + {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, + + /* terminator */ + {NULL, NULL, NULL}, +}; + +static int wm8750_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); + } + + /* set up audio path audio_mapnects */ + for(i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0}, + {11289600, 8000, 1408, 0x16, 0x0}, + {18432000, 8000, 2304, 0x7, 0x0}, + {16934400, 8000, 2112, 0x17, 0x0}, + {12000000, 8000, 1500, 0x6, 0x1}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0}, + {16934400, 11025, 1536, 0x19, 0x0}, + {12000000, 11025, 1088, 0x19, 0x1}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0}, + {18432000, 16000, 1152, 0xb, 0x0}, + {12000000, 16000, 750, 0xa, 0x1}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0}, + {16934400, 22050, 768, 0x1b, 0x0}, + {12000000, 22050, 544, 0x1b, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0}, + {18432000, 32000, 576, 0xd, 0x0}, + {12000000, 32000, 375, 0xa, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0}, + {16934400, 44100, 384, 0x11, 0x0}, + {12000000, 44100, 272, 0x11, 0x1}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0}, + {18432000, 48000, 384, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0}, + {16934400, 88200, 192, 0x1f, 0x0}, + {12000000, 88200, 136, 0x1f, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0}, + {18432000, 96000, 192, 0xf, 0x0}, + {12000000, 96000, 125, 0xe, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +/* WM8750 supports numerous input clocks per sample rate */ +static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai, + struct snd_soc_clock_info *info, unsigned int clk) +{ + dai->mclk = 0; + + /* check that the calculated FS and rate actually match a clock from + * the machine driver */ + if (info->fs * info->rate == clk) + dai->mclk = clk; + + return dai->mclk; +} + +static int wm8750_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 iface = 0, bfs, srate = 0; + int i = get_coeff(rtd->codec_dai->mclk, + snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate)); + + /* is coefficient valid ? */ + if (i < 0) + return i; + + bfs = SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs); + + /* set master/slave audio interface */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + } + + /* interface format */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + } + + /* bit size */ + switch (rtd->codec_dai->dai_runtime.pcmfmt) { + case SNDRV_PCM_FMTBIT_S16_LE: + break; + case SNDRV_PCM_FMTBIT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FMTBIT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FMTBIT_S32_LE: + iface |= 0x000c; + break; + } + + /* clock inversion */ + switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + } + + /* set bclk divisor rate */ + switch (bfs) { + case 1: + break; + case 4: + srate |= (0x1 << 7); + break; + case 8: + srate |= (0x2 << 7); + break; + case 16: + srate |= (0x3 << 7); + break; + } + + /* set iface & srate */ + wm8750_write(codec, WM8750_IFACE, iface); + wm8750_write(codec, WM8750_SRATE, srate | + (coeff_div[i].sr << 1) | coeff_div[i].usb); + + return 0; +} + +static int wm8750_mute(struct snd_soc_codec *codec, + struct snd_soc_codec_dai *dai, int mute) +{ + u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7; + if (mute) + wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8); + else + wm8750_write(codec, WM8750_ADCDAC, mute_reg); + return 0; +} + +static int wm8750_dapm_event(struct snd_soc_codec *codec, int event) +{ + u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e; + + switch (event) { + case SNDRV_CTL_POWER_D0: /* full On */ + /* set vmid to 50k and unmute dac */ + wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0); + break; + case SNDRV_CTL_POWER_D1: /* partial On */ + case SNDRV_CTL_POWER_D2: /* partial On */ + /* set vmid to 5k for quick power up */ + wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1); + break; + case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* mute dac and set vmid to 500k, enable VREF */ + wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141); + break; + case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + wm8750_write(codec, WM8750_PWR1, 0x0001); + break; + } + codec->dapm_state = event; + return 0; +} + +struct snd_soc_codec_dai wm8750_dai = { + .name = "WM8750", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + }, + .config_sysclk = wm8750_config_sysclk, + .digital_mute = wm8750_mute, + .ops = { + .prepare = wm8750_pcm_prepare, + }, + .caps = { + .num_modes = ARRAY_SIZE(wm8750_modes), + .mode = wm8750_modes, + }, +}; +EXPORT_SYMBOL_GPL(wm8750_dai); + +static void wm8750_work(void *data) +{ + struct snd_soc_codec *codec = (struct snd_soc_codec *)data; + wm8750_dapm_event(codec, codec->dapm_state); +} + +static int wm8750_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + return 0; +} + +static int wm8750_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i; + u8 data[2]; + u16 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) { + if (i == WM8750_RESET) + continue; + data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); + data[1] = cache[i] & 0x00ff; + codec->hw_write(codec->control_data, data, 2); + } + + wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* charge wm8750 caps */ + if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { + wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); + codec->dapm_state = SNDRV_CTL_POWER_D0; + queue_delayed_work(wm8750_workq, &wm8750_dapm_work, + msecs_to_jiffies(1000)); + } + + return 0; +} + +/* + * initialise the WM8750 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8750_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + int reg, ret = 0; + + codec->name = "WM8750"; + codec->owner = THIS_MODULE; + codec->read = wm8750_read_reg_cache; + codec->write = wm8750_write; + codec->dapm_event = wm8750_dapm_event; + codec->dai = &wm8750_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(wm8750_reg); + + codec->reg_cache = + kzalloc(sizeof(u16) * ARRAY_SIZE(wm8750_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + memcpy(codec->reg_cache, wm8750_reg, + sizeof(u16) * ARRAY_SIZE(wm8750_reg)); + codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8750_reg); + + wm8750_reset(codec); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + kfree(codec->reg_cache); + return ret; + } + + /* charge output caps */ + wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); + codec->dapm_state = SNDRV_CTL_POWER_D3hot; + queue_delayed_work(wm8750_workq, &wm8750_dapm_work, + msecs_to_jiffies(1000)); + + /* set the update bits */ + reg = wm8750_read_reg_cache(codec, WM8750_LDAC); + wm8750_write(codec, WM8750_LDAC, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_RDAC); + wm8750_write(codec, WM8750_RDAC, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V); + wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V); + wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V); + wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V); + wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_LINVOL); + wm8750_write(codec, WM8750_LINVOL, reg | 0x0100); + reg = wm8750_read_reg_cache(codec, WM8750_RINVOL); + wm8750_write(codec, WM8750_RINVOL, reg | 0x0100); + + wm8750_add_controls(codec); + wm8750_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) { + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + } + + return ret; +} + +/* If the i2c layer weren't so broken, we could pass this kind of data + around */ +static struct snd_soc_device *wm8750_socdev; + +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + +/* + * WM8731 2 wire address is determined by GPIO5 + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver wm8750_i2c_driver; +static struct i2c_client client_template; + +static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ + struct snd_soc_device *socdev = wm8750_socdev; + struct wm8750_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec = socdev->codec; + struct i2c_client *i2c; + int ret; + + if (addr != setup->i2c_address) + return -ENODEV; + + client_template.adapter = adap; + client_template.addr = addr; + + i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (i2c == NULL) { + kfree(codec); + return -ENOMEM; + } + memcpy(i2c, &client_template, sizeof(struct i2c_client)); + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = i2c_attach_client(i2c); + if (ret < 0) { + err("failed to attach codec at addr %x\n", addr); + goto err; + } + + ret = wm8750_init(socdev); + if (ret < 0) { + err("failed to initialise WM8750\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} + +static int wm8750_i2c_detach(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + i2c_detach_client(client); + kfree(codec->reg_cache); + kfree(client); + return 0; +} + +static int wm8750_i2c_attach(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, wm8750_codec_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver wm8750_i2c_driver = { + .driver = { + .name = "WM8750 I2C Codec", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_WM8750, + .attach_adapter = wm8750_i2c_attach, + .detach_client = wm8750_i2c_detach, + .command = NULL, +}; + +static struct i2c_client client_template = { + .name = "WM8750", + .driver = &wm8750_i2c_driver, +}; +#endif + +static int wm8750_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct wm8750_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec; + int ret = 0; + + info("WM8750 Audio Codec %s", WM8750_VERSION); + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + wm8750_socdev = socdev; + INIT_WORK(&wm8750_dapm_work, wm8750_work, codec); + wm8750_workq = create_workqueue("wm8750"); + if (wm8750_workq == NULL) { + kfree(codec); + return -ENOMEM; + } +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + if (setup->i2c_address) { + normal_i2c[0] = setup->i2c_address; + codec->hw_write = (hw_write_t)i2c_master_send; + ret = i2c_add_driver(&wm8750_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); + } +#else + /* Add other interfaces here */ +#endif + + return ret; +} + +/* power down chip */ +static int wm8750_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec->control_data) + wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + if (wm8750_workq) + destroy_workqueue(wm8750_workq); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + i2c_del_driver(&wm8750_i2c_driver); +#endif + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8750 = { + .probe = wm8750_probe, + .remove = wm8750_remove, + .suspend = wm8750_suspend, + .resume = wm8750_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750); + +MODULE_DESCRIPTION("ASoC WM8750 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h new file mode 100644 index 000000000000..ee5eea4a2d34 --- /dev/null +++ b/sound/soc/codecs/wm8750.h @@ -0,0 +1,66 @@ +/* + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on WM8753.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _WM8750_H +#define _WM8750_H + +/* WM8750 register space */ + +#define WM8750_LINVOL 0x00 +#define WM8750_RINVOL 0x01 +#define WM8750_LOUT1V 0x02 +#define WM8750_ROUT1V 0x03 +#define WM8750_ADCDAC 0x05 +#define WM8750_IFACE 0x07 +#define WM8750_SRATE 0x08 +#define WM8750_LDAC 0x0a +#define WM8750_RDAC 0x0b +#define WM8750_BASS 0x0c +#define WM8750_TREBLE 0x0d +#define WM8750_RESET 0x0f +#define WM8750_3D 0x10 +#define WM8750_ALC1 0x11 +#define WM8750_ALC2 0x12 +#define WM8750_ALC3 0x13 +#define WM8750_NGATE 0x14 +#define WM8750_LADC 0x15 +#define WM8750_RADC 0x16 +#define WM8750_ADCTL1 0x17 +#define WM8750_ADCTL2 0x18 +#define WM8750_PWR1 0x19 +#define WM8750_PWR2 0x1a +#define WM8750_ADCTL3 0x1b +#define WM8750_ADCIN 0x1f +#define WM8750_LADCIN 0x20 +#define WM8750_RADCIN 0x21 +#define WM8750_LOUTM1 0x22 +#define WM8750_LOUTM2 0x23 +#define WM8750_ROUTM1 0x24 +#define WM8750_ROUTM2 0x25 +#define WM8750_MOUTM1 0x26 +#define WM8750_MOUTM2 0x27 +#define WM8750_LOUT2V 0x28 +#define WM8750_ROUT2V 0x29 +#define WM8750_MOUTV 0x2a + +#define WM8750_CACHE_REGNUM 0x2a + +struct wm8750_setup_data { + unsigned short i2c_address; + unsigned int mclk; +}; + +extern struct snd_soc_codec_dai wm8750_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8750; + +#endif -- cgit v1.2.3-59-g8ed1b From 10c5cf30446fe91b7173436b75c4f00dfb4cd9f8 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:37:32 +0200 Subject: [ALSA] ASoC codecs: WM9712 support This patch adds ASoC support for the WM9712 codec. Supported features:- o Capture/Playback/Sidetone/Bypass. o Aux DAC. o 8k - 48k sample rates. o DAPM. Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm9712.c | 778 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm9712.h | 14 + 2 files changed, 792 insertions(+) create mode 100644 sound/soc/codecs/wm9712.c create mode 100644 sound/soc/codecs/wm9712.h diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c new file mode 100644 index 000000000000..4850550e2e31 --- /dev/null +++ b/sound/soc/codecs/wm9712.c @@ -0,0 +1,778 @@ +/* + * wm9712.c -- ALSA Soc WM9712 codec support + * + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * 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. + * + * Revision history + * 4th Feb 2006 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM9712_VERSION "0.4" + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg); +static int ac97_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val); + +#define AC97_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define AC97_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +/* may need to expand this */ +static struct snd_soc_dai_mode ac97_modes[] = { + {0, 0, SNDRV_PCM_FMTBIT_S16_LE, AC97_RATES}, + {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, AC97_RATES}, +}; + +/* + * WM9712 register cache + */ +static const u16 wm9712_reg[] = { + 0x6174, 0x8000, 0x8000, 0x8000, // 6 + 0xf0f0, 0xaaa0, 0xc008, 0x6808, // e + 0xe808, 0xaaa0, 0xad00, 0x8000, // 16 + 0xe808, 0x3000, 0x8000, 0x0000, // 1e + 0x0000, 0x0000, 0x0000, 0x000f, // 26 + 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e + 0x0000, 0xbb80, 0x0000, 0x0000, // 36 + 0x0000, 0x2000, 0x0000, 0x0000, // 3e + 0x0000, 0x0000, 0x0000, 0x0000, // 46 + 0x0000, 0x0000, 0xf83e, 0xffff, // 4e + 0x0000, 0x0000, 0x0000, 0xf83e, // 56 + 0x0008, 0x0000, 0x0000, 0x0000, // 5e + 0xb032, 0x3e00, 0x0000, 0x0000, // 66 + 0x0000, 0x0000, 0x0000, 0x0000, // 6e + 0x0000, 0x0000, 0x0000, 0x0006, // 76 + 0x0001, 0x0000, 0x574d, 0x4c12, // 7e + 0x0000, 0x0000 // virtual hp mixers +}; + +/* virtual HP mixers regs */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 + +static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; +static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right", + "Mono"}; +static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"}; +static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2", + "Stereo"}; +static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer", + "Line", "Headphone Mixer", "Phone Mixer", "Phone"}; +static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"}; +static const char *wm9712_diff_sel[] = {"Mic", "Line"}; + +static const struct soc_enum wm9712_enum[] = { +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select), +SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux), +SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src), +SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src), +SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc), +SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base), +SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain), +SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic), +SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel), +SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel), +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type), +SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel), +}; + +static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1), + +SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), +SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), +SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", wm9712_enum[0]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", wm9712_enum[10]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1), + +SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1), +SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1), + +SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1), +SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1), +SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1), + +SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1), +SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1), +SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1), +SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), +SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1), + +SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0), +SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1), + +SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), +SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1), +SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0), + +SOC_ENUM("Bass Control", wm9712_enum[5]), +SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0), +SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0), + +SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", wm9712_enum[6]), +SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1), +SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0), + +SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), +SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), +SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0), +}; + +/* add non dapm controls */ +static int wm9712_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path. + */ +static int mixer_event (struct snd_soc_dapm_widget *w, int event) +{ + u16 l, r, beep, line, phone, mic, pcm, aux; + + l = ac97_read(w->codec, HPL_MIXER); + r = ac97_read(w->codec, HPR_MIXER); + beep = ac97_read(w->codec, AC97_PC_BEEP); + mic = ac97_read(w->codec, AC97_VIDEO); + phone = ac97_read(w->codec, AC97_PHONE); + line = ac97_read(w->codec, AC97_LINE); + pcm = ac97_read(w->codec, AC97_PCM); + aux = ac97_read(w->codec, AC97_CD); + + if (l & 0x1 || r & 0x1) + ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff); + else + ac97_write(w->codec, AC97_VIDEO, mic | 0x8000); + + if (l & 0x2 || r & 0x2) + ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); + else + ac97_write(w->codec, AC97_PCM, pcm | 0x8000); + + if (l & 0x4 || r & 0x4) + ac97_write(w->codec, AC97_LINE, line & 0x7fff); + else + ac97_write(w->codec, AC97_LINE, line | 0x8000); + + if (l & 0x8 || r & 0x8) + ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); + else + ac97_write(w->codec, AC97_PHONE, phone | 0x8000); + + if (l & 0x10 || r & 0x10) + ac97_write(w->codec, AC97_CD, aux & 0x7fff); + else + ac97_write(w->codec, AC97_CD, aux | 0x8000); + + if (l & 0x20 || r & 0x20) + ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); + else + ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); + + return 0; +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0), +}; + +/* Speaker Mixer */ +static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1), + SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1), + SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1), + SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1), + SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1), +}; + +/* Phone Mixer */ +static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1), + SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1), + SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1), + SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1), + SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1), + SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1), +}; + +/* ALC headphone mux */ +static const struct snd_kcontrol_new wm9712_alc_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[1]); + +/* out 3 mux */ +static const struct snd_kcontrol_new wm9712_out3_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[2]); + +/* spk mux */ +static const struct snd_kcontrol_new wm9712_spk_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[3]); + +/* Capture to Phone mux */ +static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[4]); + +/* Capture left select */ +static const struct snd_kcontrol_new wm9712_capture_selectl_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[8]); + +/* Capture right select */ +static const struct snd_kcontrol_new wm9712_capture_selectr_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[9]); + +/* Mic select */ +static const struct snd_kcontrol_new wm9712_mic_src_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[7]); + +/* diff select */ +static const struct snd_kcontrol_new wm9712_diff_sel_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[11]); + +static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = { +SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm9712_alc_mux_controls), +SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, + &wm9712_out3_mux_controls), +SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0, + &wm9712_spk_mux_controls), +SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0, + &wm9712_capture_phone_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0, + &wm9712_capture_selectl_controls), +SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0, + &wm9712_capture_selectr_controls), +SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0, + &wm9712_mic_src_controls), +SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, + &wm9712_diff_sel_controls), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, + &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1, + &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, + &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, + &wm9712_speaker_mixer_controls[0], + ARRAY_SIZE(wm9712_speaker_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1), +SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1), +SND_SOC_DAPM_OUTPUT("MONOOUT"), +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LOUT2"), +SND_SOC_DAPM_OUTPUT("ROUT2"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_INPUT("LINEINL"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("PHONE"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +}; + +static const char *audio_map[][3] = { + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + + /* Left HP mixer */ + {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Left HP Mixer", "Line Bypass Switch", "Line PGA"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"}, + {"Left HP Mixer", NULL, "ALC Sidetone Mux"}, + //{"Right HP Mixer", NULL, "HP Mixer"}, + + /* Right HP mixer */ + {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Right HP Mixer", "Line Bypass Switch", "Line PGA"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"}, + {"Right HP Mixer", NULL, "ALC Sidetone Mux"}, + + /* speaker mixer */ + {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Speaker Mixer", "Line Bypass Switch", "Line PGA"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + + /* Phone mixer */ + {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Phone Mixer", "Line Bypass Switch", "Line PGA"}, + {"Phone Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"}, + {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"}, + + /* inputs */ + {"Line PGA", NULL, "LINEINL"}, + {"Line PGA", NULL, "LINEINR"}, + {"Phone PGA", NULL, "PHONE"}, + {"Mic PGA", NULL, "MIC1"}, + {"Mic PGA", NULL, "MIC2"}, + + /* left capture selector */ + {"Left Capture Select", "Mic", "MIC1"}, + {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"}, + {"Left Capture Select", "Line", "LINEINL"}, + {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"}, + {"Left Capture Select", "Phone Mixer", "Phone Mixer"}, + {"Left Capture Select", "Phone", "PHONE"}, + + /* right capture selector */ + {"Right Capture Select", "Mic", "MIC2"}, + {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"}, + {"Right Capture Select", "Line", "LINEINR"}, + {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"}, + {"Right Capture Select", "Phone Mixer", "Phone Mixer"}, + {"Right Capture Select", "Phone", "PHONE"}, + + /* ALC Sidetone */ + {"ALC Sidetone Mux", "Stereo", "Left Capture Select"}, + {"ALC Sidetone Mux", "Stereo", "Right Capture Select"}, + {"ALC Sidetone Mux", "Left", "Left Capture Select"}, + {"ALC Sidetone Mux", "Right", "Right Capture Select"}, + + /* ADC's */ + {"Left ADC", NULL, "Left Capture Select"}, + {"Right ADC", NULL, "Right Capture Select"}, + + /* outputs */ + {"MONOOUT", NULL, "Phone Mixer"}, + {"HPOUTL", NULL, "Headphone PGA"}, + {"Headphone PGA", NULL, "Left HP Mixer"}, + {"HPOUTR", NULL, "Headphone PGA"}, + {"Headphone PGA", NULL, "Right HP Mixer"}, + + /* mono hp mixer */ + {"Mono HP Mixer", NULL, "Left HP Mixer"}, + {"Mono HP Mixer", NULL, "Right HP Mixer"}, + + /* Out3 Mux */ + {"Out3 Mux", "Left", "Left HP Mixer"}, + {"Out3 Mux", "Mono", "Phone Mixer"}, + {"Out3 Mux", "Left + Right", "Mono HP Mixer"}, + {"Out 3 PGA", NULL, "Out3 Mux"}, + {"OUT3", NULL, "Out 3 PGA"}, + + /* speaker Mux */ + {"Speaker Mux", "Speaker Mix", "Speaker Mixer"}, + {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"}, + {"Speaker PGA", NULL, "Speaker Mux"}, + {"LOUT2", NULL, "Speaker PGA"}, + {"ROUT2", NULL, "Speaker PGA"}, + + {NULL, NULL, NULL}, +}; + +static int wm9712_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]); + } + + /* set up audio path audio_mapnects */ + for(i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || + reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || + reg == AC97_REC_GAIN) + return soc_ac97_ops.read(codec->ac97, reg); + else { + reg = reg >> 1; + + if (reg > (ARRAY_SIZE(wm9712_reg))) + return -EIO; + + return cache[reg]; + } +} + +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + u16 *cache = codec->reg_cache; + + soc_ac97_ops.write(codec->ac97, reg, val); + reg = reg >> 1; + if (reg <= (ARRAY_SIZE(wm9712_reg))) + cache[reg] = val; + + return 0; +} + +static int ac97_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + int reg; + u16 vra; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return ac97_write(codec, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 vra, xsle; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + xsle = ac97_read(codec, AC97_PCI_SID); + ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +struct snd_soc_codec_dai wm9712_dai[] = { +{ + .name = "AC97 HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .prepare = ac97_prepare,}, + .caps = { + .num_modes = ARRAY_SIZE(ac97_modes), + .mode = ac97_modes,}, + }, + { + .name = "AC97 Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1,}, + .ops = { + .prepare = ac97_aux_prepare,}, + .caps = { + .num_modes = ARRAY_SIZE(ac97_modes), + .mode = ac97_modes,}, + }, +}; +EXPORT_SYMBOL_GPL(wm9712_dai); + +static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) +{ + u16 reg; + + switch (event) { + case SNDRV_CTL_POWER_D0: /* full On */ + /* liam - maybe enable thermal shutdown */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + break; + case SNDRV_CTL_POWER_D1: /* partial On */ + case SNDRV_CTL_POWER_D2: /* partial On */ + break; + case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* enable master bias and vmid */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + /* disable everything including AC link */ + ac97_write(codec, AC97_EXTENDED_MID, 0xffff); + ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + ac97_write(codec, AC97_POWERDOWN, 0xffff); + break; + } + codec->dapm_state = event; + return 0; +} + +static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) +{ + if (try_warm && soc_ac97_ops.warm_reset) { + soc_ac97_ops.warm_reset(codec->ac97); + if (!(ac97_read(codec, 0) & 0x8000)) + return 1; + } + + soc_ac97_ops.reset(codec->ac97); + if (ac97_read(codec, 0) & 0x8000) + goto err; + return 0; + +err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; +} + +static int wm9712_soc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + return 0; +} + +static int wm9712_soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i, ret; + u16 *cache = codec->reg_cache; + + ret = wm9712_reset(codec, 1); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; + } + + wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + if (ret == 0) { + /* Sync reg_cache with the hardware after cold reset */ + for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) { + if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || + (i > 0x58 && i != 0x5c)) + continue; + soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); + } + } + + if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) + wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0); + + return ret; +} + +static int wm9712_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION); + + socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (socdev->codec == NULL) + return -ENOMEM; + codec = socdev->codec; + mutex_init(&codec->mutex); + + codec->reg_cache = + kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) { + kfree(codec->ac97); + kfree(socdev->codec); + socdev->codec = NULL; + return -ENOMEM; + } + memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg)); + codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg); + codec->reg_cache_step = 2; + + codec->name = "WM9712"; + codec->owner = THIS_MODULE; + codec->dai = wm9712_dai; + codec->num_dai = ARRAY_SIZE(wm9712_dai); + codec->write = ac97_write; + codec->read = ac97_read; + codec->dapm_event = wm9712_dapm_event; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); + if (ret < 0) + goto err; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) + goto pcm_err; + + ret = wm9712_reset(codec, 0); + if (ret < 0) { + printk(KERN_ERR "AC97 link error\n"); + goto reset_err; + } + + /* set alc mux to none */ + ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); + + wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm9712_add_controls(codec); + wm9712_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) + goto reset_err; + + return 0; + +reset_err: + snd_soc_free_pcms(socdev); + +pcm_err: + snd_soc_free_ac97_codec(codec); + +err: + kfree(socdev->codec->reg_cache); + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int wm9712_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec == NULL) + return 0; + + snd_soc_dapm_free(socdev); + snd_soc_free_pcms(socdev); + snd_soc_free_ac97_codec(codec); + kfree(codec->reg_cache); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm9712 = { + .probe = wm9712_soc_probe, + .remove = wm9712_soc_remove, + .suspend = wm9712_soc_suspend, + .resume = wm9712_soc_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712); + +MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h new file mode 100644 index 000000000000..719105d61e65 --- /dev/null +++ b/sound/soc/codecs/wm9712.h @@ -0,0 +1,14 @@ +/* + * wm9712.h -- WM9712 Soc Audio driver + */ + +#ifndef _WM9712_H +#define _WM9712_H + +#define WM9712_DAI_AC97_HIFI 0 +#define WM9712_DAI_AC97_AUX 1 + +extern struct snd_soc_codec_dai wm9712_dai[2]; +extern struct snd_soc_codec_device soc_codec_dev_wm9712; + +#endif -- cgit v1.2.3-59-g8ed1b From dbc6b6ad767c86907db373e85139b0e975ba7599 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:38:03 +0200 Subject: [ALSA] ASoC codecs: generic AC97 support This patch allows the std Alsa AC97 codec driver to use any AsoC AC97 controller driver. Currently, only HiFi playback and Capture are supported atm. Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/ac97.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ac97.h | 18 ++++++ 2 files changed, 185 insertions(+) create mode 100644 sound/soc/codecs/ac97.c create mode 100644 sound/soc/codecs/ac97.h diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c new file mode 100644 index 000000000000..dd1a9f579a6b --- /dev/null +++ b/sound/soc/codecs/ac97.c @@ -0,0 +1,167 @@ +/* + * ac97.c -- ALSA Soc AC97 codec support + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * 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. + * + * Revision history + * 17th Oct 2005 Initial version. + * + * Generic AC97 support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AC97_VERSION "0.5" + +#define AC97_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define AC97_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +/* may need to expand this */ +static struct snd_soc_dai_mode soc_ac97[] = { + {0, 0, SNDRV_PCM_FMTBIT_S16_LE, AC97_RATES}, + {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, AC97_RATES}, + {0, 0, SNDRV_PCM_FMTBIT_S20_3LE, AC97_RATES}, +}; + +static int ac97_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + + int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; + return snd_ac97_set_rate(codec->ac97, reg, runtime->rate); +} + +static struct snd_soc_codec_dai ac97_dai = { + .name = "AC97 HiFi", + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .prepare = ac97_prepare,}, + .caps = { + .num_modes = ARRAY_SIZE(soc_ac97), + .mode = soc_ac97,}, +}; + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + return soc_ac97_ops.read(codec->ac97, reg); +} + +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + soc_ac97_ops.write(codec->ac97, reg, val); + return 0; +} + +static int ac97_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97_template ac97_template; + int ret = 0; + + printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION); + + socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (socdev->codec == NULL) + return -ENOMEM; + codec = socdev->codec; + mutex_init(&codec->mutex); + + codec->name = "AC97"; + codec->owner = THIS_MODULE; + codec->dai = &ac97_dai; + codec->num_dai = 1; + codec->write = ac97_write; + codec->read = ac97_read; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if(ret < 0) + goto err; + + /* add codec as bus device for standard ac97 */ + ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus); + if(ret < 0) + goto bus_err; + + memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); + ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97); + if(ret < 0) + goto bus_err; + + ret = snd_soc_register_card(socdev); + if (ret < 0) + goto bus_err; + return 0; + +bus_err: + snd_soc_free_pcms(socdev); + +err: + kfree(socdev->codec->reg_cache); + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int ac97_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if(codec == NULL) + return 0; + + snd_soc_free_pcms(socdev); + kfree(socdev->codec->reg_cache); + kfree(socdev->codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ac97= { + .probe = ac97_soc_probe, + .remove = ac97_soc_remove, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_ac97); + +MODULE_DESCRIPTION("Soc Generic AC97 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h new file mode 100644 index 000000000000..930ddfc2321a --- /dev/null +++ b/sound/soc/codecs/ac97.h @@ -0,0 +1,18 @@ +/* + * linux/sound/codecs/ac97.h -- ALSA SoC Layer + * + * Author: Liam Girdwood + * Created: Dec 1st 2005 + * Copyright: Wolfson Microelectronics. PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_SOC_AC97_H +#define __LINUX_SND_SOC_AC97_H + +extern struct snd_soc_codec_device soc_codec_dev_ac97; + +#endif -- cgit v1.2.3-59-g8ed1b From 7f137ab673124ee0a210ab5b74c1f7234d6145fa Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Fri, 6 Oct 2006 18:38:37 +0200 Subject: [ALSA] ASoC codecs: build files This patch adds an ASoC Makefile and Kconfig for the WM8731, WM8750 and WM9712 codecs. Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/Kconfig | 6 ++++++ sound/soc/Makefile | 2 +- sound/soc/codecs/Kconfig | 15 +++++++++++++++ sound/soc/codecs/Makefile | 9 +++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/Kconfig create mode 100644 sound/soc/codecs/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 200709f4b708..288bad30b213 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -5,6 +5,9 @@ menu "SoC audio support" depends on SND!=n +config SND_SOC_AC97_BUS + bool + config SND_SOC tristate "SoC audio support" ---help--- @@ -16,4 +19,7 @@ config SND_SOC This SoC audio support can also be built as a module. If so, the module will be called snd-soc-core. +# Supported codecs +source "sound/soc/codecs/Kconfig" + endmenu diff --git a/sound/soc/Makefile b/sound/soc/Makefile index b211ee63fd76..3dd4f20c5ecf 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o - +obj-$(CONFIG_SND_SOC) += codecs/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig new file mode 100644 index 000000000000..78ac2688e124 --- /dev/null +++ b/sound/soc/codecs/Kconfig @@ -0,0 +1,15 @@ +config SND_SOC_AC97_CODEC + tristate + depends SND_SOC + +config SND_SOC_WM8731 + tristate + depends SND_SOC + +config SND_SOC_WM8750 + tristate + depends SND_SOC + +config SND_SOC_WM9712 + tristate + depends SND_SOC diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile new file mode 100644 index 000000000000..3249a6e4f1d0 --- /dev/null +++ b/sound/soc/codecs/Makefile @@ -0,0 +1,9 @@ +snd-soc-ac97-objs := ac97.o +snd-soc-wm8731-objs := wm8731.o +snd-soc-wm8750-objs := wm8750.o +snd-soc-wm9712-objs := wm9712.o + +obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o +obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o +obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o +obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o -- cgit v1.2.3-59-g8ed1b From ff9abf5b0a655b59d59ea61aec5be6285bf3ac30 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 6 Oct 2006 18:39:29 +0200 Subject: [ALSA] ASoC AT91RM92000 audio DMA This patch adds ASoC audio DMA support to the Atmel AT91RM9200 CPU. Features:- o Playback/Capture supported. o 16 Bit data size. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-pcm.c | 428 ++++++++++++++++++++++++++++++++++++++++ sound/soc/at91/at91rm9200-pcm.h | 75 +++++++ 2 files changed, 503 insertions(+) create mode 100644 sound/soc/at91/at91rm9200-pcm.c create mode 100644 sound/soc/at91/at91rm9200-pcm.h diff --git a/sound/soc/at91/at91rm9200-pcm.c b/sound/soc/at91/at91rm9200-pcm.c new file mode 100644 index 000000000000..237bc5f07579 --- /dev/null +++ b/sound/soc/at91/at91rm9200-pcm.c @@ -0,0 +1,428 @@ +/* + * at91rm9200-pcm.c -- ALSA PCM interface for the Atmel AT91RM9200 chip. + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Mar 3, 2006 + * + * Based on pxa2xx-pcm.c by: + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "at91rm9200-pcm.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO "at91rm9200-pcm: " x) +#else +#define DBG(x...) +#endif + +static const snd_pcm_hardware_t at91rm9200_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 32 * 1024, +}; + +struct at91rm9200_runtime_data { + at91rm9200_pcm_dma_params_t *params; + dma_addr_t dma_buffer; /* physical address of dma buffer */ + dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ + size_t period_size; + dma_addr_t period_ptr; /* physical address of next period */ + u32 pdc_xpr_save; /* PDC register save */ + u32 pdc_xcr_save; + u32 pdc_xnpr_save; + u32 pdc_xncr_save; +}; + +static void at91rm9200_pcm_dma_irq(u32 ssc_sr, + struct snd_pcm_substream *substream) +{ + struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; + at91rm9200_pcm_dma_params_t *params = prtd->params; + static int count = 0; + + count++; + + if (ssc_sr & params->mask->ssc_endbuf) { + + printk(KERN_WARNING + "at91rm9200-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK + ? "underrun" : "overrun", + params->name, ssc_sr, count); + + /* re-start the PDC */ + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); + + prtd->period_ptr += prtd->period_size; + if (prtd->period_ptr >= prtd->dma_buffer_end) { + prtd->period_ptr = prtd->dma_buffer; + } + + at91_ssc_write(params->pdc->xpr, prtd->period_ptr); + at91_ssc_write(params->pdc->xcr, + prtd->period_size / params->pdc_xfer_size); + + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); + } + + if (ssc_sr & params->mask->ssc_endx) { + + /* Load the PDC next pointer and counter registers */ + prtd->period_ptr += prtd->period_size; + if (prtd->period_ptr >= prtd->dma_buffer_end) { + prtd->period_ptr = prtd->dma_buffer; + } + at91_ssc_write(params->pdc->xnpr, prtd->period_ptr); + at91_ssc_write(params->pdc->xncr, + prtd->period_size / params->pdc_xfer_size); + } + + snd_pcm_period_elapsed(substream); +} + +static int at91rm9200_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + struct at91rm9200_runtime_data *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + /* this may get called several times by oss emulation + * with different params */ + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + + prtd->params = rtd->cpu_dai->dma_data; + prtd->params->dma_intr_handler = at91rm9200_pcm_dma_irq; + + prtd->dma_buffer = runtime->dma_addr; + prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; + prtd->period_size = params_period_bytes(params); + + DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n", + prtd->params->name, runtime->dma_bytes, prtd->period_size); + return 0; +} + +static int at91rm9200_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; + at91rm9200_pcm_dma_params_t *params = prtd->params; + + if (params != NULL) { + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); + prtd->params->dma_intr_handler = NULL; + } + + return 0; +} + +static int at91rm9200_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; + at91rm9200_pcm_dma_params_t *params = prtd->params; + + at91_ssc_write(params->ssc->idr, + params->mask->ssc_endx | params->mask->ssc_endbuf); + + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); + return 0; +} + +static int at91rm9200_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; + at91rm9200_pcm_dma_params_t *params = prtd->params; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->period_ptr = prtd->dma_buffer; + + at91_ssc_write(params->pdc->xpr, prtd->period_ptr); + at91_ssc_write(params->pdc->xcr, + prtd->period_size / params->pdc_xfer_size); + + prtd->period_ptr += prtd->period_size; + at91_ssc_write(params->pdc->xnpr, prtd->period_ptr); + at91_ssc_write(params->pdc->xncr, + prtd->period_size / params->pdc_xfer_size); + + DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n", + (unsigned long) prtd->period_ptr, + at91_ssc_read(params->pdc->xpr), + at91_ssc_read(params->pdc->xcr), + at91_ssc_read(params->pdc->xnpr), + at91_ssc_read(params->pdc->xncr)); + + at91_ssc_write(params->ssc->ier, + params->mask->ssc_endx | params->mask->ssc_endbuf); + + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); + + DBG("sr=%lx imr=%lx\n", at91_ssc_read(params->ssc->ier - 4), + at91_ssc_read(params->ssc->ier + 8)); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); + break; + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t at91rm9200_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct at91rm9200_runtime_data *prtd = runtime->private_data; + at91rm9200_pcm_dma_params_t *params = prtd->params; + dma_addr_t ptr; + snd_pcm_uframes_t x; + + ptr = (dma_addr_t) at91_ssc_read(params->pdc->xpr); + x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); + + if (x == runtime->buffer_size) + x = 0; + return x; +} + +static int at91rm9200_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct at91rm9200_runtime_data *prtd; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &at91rm9200_pcm_hardware); + + /* ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + + prtd = kzalloc(sizeof(struct at91rm9200_runtime_data), GFP_KERNEL); + if (prtd == NULL) { + ret = -ENOMEM; + goto out; + } + runtime->private_data = prtd; + + out: + return ret; +} + +static int at91rm9200_pcm_close(struct snd_pcm_substream *substream) +{ + struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; + + kfree(prtd); + return 0; +} + +static int at91rm9200_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +struct snd_pcm_ops at91rm9200_pcm_ops = { + .open = at91rm9200_pcm_open, + .close = at91rm9200_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = at91rm9200_pcm_hw_params, + .hw_free = at91rm9200_pcm_hw_free, + .prepare = at91rm9200_pcm_prepare, + .trigger = at91rm9200_pcm_trigger, + .pointer = at91rm9200_pcm_pointer, + .mmap = at91rm9200_pcm_mmap, +}; + +static int at91rm9200_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = at91rm9200_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + + DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *) buf->area, + (void *) buf->addr, + size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static u64 at91rm9200_pcm_dmamask = 0xffffffff; + +static int at91rm9200_pcm_new(struct snd_card *card, + struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &at91rm9200_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = at91rm9200_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = at91rm9200_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +static void at91rm9200_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static int at91rm9200_pcm_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct snd_pcm_runtime *runtime = dai->runtime; + struct at91rm9200_runtime_data *prtd; + at91rm9200_pcm_dma_params_t *params; + + if (!runtime) + return 0; + + prtd = runtime->private_data; + params = prtd->params; + + /* disable the PDC and save the PDC registers */ + + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); + + prtd->pdc_xpr_save = at91_ssc_read(params->pdc->xpr); + prtd->pdc_xcr_save = at91_ssc_read(params->pdc->xcr); + prtd->pdc_xnpr_save = at91_ssc_read(params->pdc->xnpr); + prtd->pdc_xncr_save = at91_ssc_read(params->pdc->xncr); + + return 0; +} + +static int at91rm9200_pcm_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct snd_pcm_runtime *runtime = dai->runtime; + struct at91rm9200_runtime_data *prtd; + at91rm9200_pcm_dma_params_t *params; + + if (!runtime) + return 0; + + prtd = runtime->private_data; + params = prtd->params; + + /* restore the PDC registers and enable the PDC */ + at91_ssc_write(params->pdc->xpr, prtd->pdc_xpr_save); + at91_ssc_write(params->pdc->xcr, prtd->pdc_xcr_save); + at91_ssc_write(params->pdc->xnpr, prtd->pdc_xnpr_save); + at91_ssc_write(params->pdc->xncr, prtd->pdc_xncr_save); + + at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); + return 0; +} + +struct snd_soc_platform at91rm9200_soc_platform = { + .name = "at91rm9200-audio", + .pcm_ops = &at91rm9200_pcm_ops, + .pcm_new = at91rm9200_pcm_new, + .pcm_free = at91rm9200_pcm_free_dma_buffers, + .suspend = at91rm9200_pcm_suspend, + .resume = at91rm9200_pcm_resume, +}; + +EXPORT_SYMBOL_GPL(at91rm9200_soc_platform); + +MODULE_AUTHOR("Frank Mandarino "); +MODULE_DESCRIPTION("Atmel AT91RM9200 PCM module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91rm9200-pcm.h b/sound/soc/at91/at91rm9200-pcm.h new file mode 100644 index 000000000000..65468f173771 --- /dev/null +++ b/sound/soc/at91/at91rm9200-pcm.h @@ -0,0 +1,75 @@ +/* + * at91rm9200-pcm.h - ALSA PCM interface for the Atmel AT91RM9200 chip + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Mar 3, 2006 + * + * Based on pxa2xx-pcm.h by: + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Registers and status bits that are required by the PCM driver. + */ +struct at91rm9200_ssc_regs { + void __iomem *cr; /* SSC control */ + void __iomem *ier; /* SSC interrupt enable */ + void __iomem *idr; /* SSC interrupt disable */ +}; + +struct at91rm9200_pdc_regs { + void __iomem *xpr; /* PDC recv/trans pointer */ + void __iomem *xcr; /* PDC recv/trans counter */ + void __iomem *xnpr; /* PDC next recv/trans pointer */ + void __iomem *xncr; /* PDC next recv/trans counter */ + void __iomem *ptcr; /* PDC transfer control */ +}; + +struct at91rm9200_ssc_mask { + u32 ssc_enable; /* SSC recv/trans enable */ + u32 ssc_disable; /* SSC recv/trans disable */ + u32 ssc_endx; /* SSC ENDTX or ENDRX */ + u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ + u32 pdc_enable; /* PDC recv/trans enable */ + u32 pdc_disable; /* PDC recv/trans disable */ +}; + + +/* + * This structure, shared between the PCM driver and the interface, + * contains all information required by the PCM driver to perform the + * PDC DMA operation. All fields except dma_intr_handler() are initialized + * by the interface. The dms_intr_handler() pointer is set by the PCM + * driver and called by the interface SSC interrupt handler if it is + * non-NULL. + */ +typedef struct { + char *name; /* stream identifier */ + int pdc_xfer_size; /* PDC counter increment in bytes */ + struct at91rm9200_ssc_regs *ssc; /* SSC register addresses */ + struct at91rm9200_pdc_regs *pdc; /* PDC receive/transmit registers */ + struct at91rm9200_ssc_mask *mask;/* SSC & PDC status bits */ + snd_pcm_substream_t *substream; + void (*dma_intr_handler)(u32, snd_pcm_substream_t *); +} at91rm9200_pcm_dma_params_t; + +extern struct snd_soc_cpu_dai at91rm9200_i2s_dai[3]; +extern struct snd_soc_platform at91rm9200_soc_platform; + + +/* + * SSC I/O helpers. + * E.g., at91_ssc_write(AT91_SSC(1) + AT91_SSC_CR, AT91_SSC_RXEN); + */ +#define AT91_SSC(x) (((x)==0) ? AT91_VA_BASE_SSC0 :\ + ((x)==1) ? AT91_VA_BASE_SSC1 : ((x)==2) ? AT91_VA_BASE_SSC2 : NULL) +#define at91_ssc_read(a) ((unsigned long) __raw_readl(a)) +#define at91_ssc_write(a,v) __raw_writel((v),(a)) -- cgit v1.2.3-59-g8ed1b From 0cbbec0984f10f216ed8332e0d39ac93cbe33a0b Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 6 Oct 2006 18:40:25 +0200 Subject: [ALSA] ASoC AT91RM92000 I2S support This patch adds I2S support to the Atmel AT91RM9200 CPU. Features:- o Playback/Capture supported. o 16 Bit data size. o 8k - 48k sample rates. o ssc0, ssc1 and ssc2 supported as I2S ports. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 688 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 sound/soc/at91/at91rm9200-i2s.c diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c new file mode 100644 index 000000000000..a74c5d85589b --- /dev/null +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -0,0 +1,688 @@ +/* + * at91rm9200-i2s.c -- ALSA Soc Audio Layer Platform driver and DMA engine + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * + * Based on pxa2xx Platform drivers by + * Liam Girdwood + * + * 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. + * + * Revision history + * 3rd Mar 2006 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "at91rm9200-pcm.h" + +#if 0 +#define DBG(x...) printk(KERN_DEBUG "at91rm9200-i2s:" x) +#else +#define DBG(x...) +#endif + +#define AT91RM9200_I2S_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) + +#define AT91RM9200_I2S_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +/* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */ +static struct snd_soc_dai_mode at91rm9200_i2s[] = { + + /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ + { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_8000, AT91RM9200_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 1500, SND_SOC_FSBD(10), (25 << 16 | 74) }, + + /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ + { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_16000, AT91RM9200_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 750, SND_SOC_FSBD(3) , (7 << 16 | 133) }, + + /* 24k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ + { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_22050, AT91RM9200_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 500, SND_SOC_FSBD(10), (25 << 16 | 24) }, + + /* 48kHz: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ + { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_48000, AT91RM9200_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 250, SND_SOC_FSBD(5), (13 << 16 | 23) }, +}; + + +/* + * SSC registers required by the PCM DMA engine. + */ +static struct at91rm9200_ssc_regs ssc_reg[3] = { + { + .cr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_CR), + .ier = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IER), + .idr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IDR), + }, + { + .cr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_CR), + .ier = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IER), + .idr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IDR), + }, + { + .cr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_CR), + .ier = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IER), + .idr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IDR), + }, +}; + +static struct at91rm9200_pdc_regs pdc_tx_reg[3] = { + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), + }, + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), + }, + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), + }, +}; + +static struct at91rm9200_pdc_regs pdc_rx_reg[3] = { + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), + }, + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), + }, + { + .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RPR), + .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RCR), + .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNPR), + .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNCR), + .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), + }, +}; + +/* + * SSC & PDC status bits for transmit and receive. + */ +static struct at91rm9200_ssc_mask ssc_tx_mask = { + .ssc_enable = AT91_SSC_TXEN, + .ssc_disable = AT91_SSC_TXDIS, + .ssc_endx = AT91_SSC_ENDTX, + .ssc_endbuf = AT91_SSC_TXBUFE, + .pdc_enable = AT91_PDC_TXTEN, + .pdc_disable = AT91_PDC_TXTDIS, +}; + +static struct at91rm9200_ssc_mask ssc_rx_mask = { + .ssc_enable = AT91_SSC_RXEN, + .ssc_disable = AT91_SSC_RXDIS, + .ssc_endx = AT91_SSC_ENDRX, + .ssc_endbuf = AT91_SSC_RXBUFF, + .pdc_enable = AT91_PDC_RXTEN, + .pdc_disable = AT91_PDC_RXTDIS, +}; + +/* + * A MUTEX is used to protect an SSC initialzed flag which allows + * the substream hw_params() call to initialize the SSC only if + * there are no other substreams open. If there are other + * substreams open, the hw_param() call can only check that + * it is using the same format and rate. + */ +static DECLARE_MUTEX(ssc0_mutex); +static DECLARE_MUTEX(ssc1_mutex); +static DECLARE_MUTEX(ssc2_mutex); + +/* + * DMA parameters. + */ +static at91rm9200_pcm_dma_params_t ssc_dma_params[3][2] = { + {{ + .name = "SSC0/I2S PCM Stereo out", + .ssc = &ssc_reg[0], + .pdc = &pdc_tx_reg[0], + .mask = &ssc_tx_mask, + }, + { + .name = "SSC0/I2S PCM Stereo in", + .ssc = &ssc_reg[0], + .pdc = &pdc_rx_reg[0], + .mask = &ssc_rx_mask, + }}, + {{ + .name = "SSC1/I2S PCM Stereo out", + .ssc = &ssc_reg[1], + .pdc = &pdc_tx_reg[1], + .mask = &ssc_tx_mask, + }, + { + .name = "SSC1/I2S PCM Stereo in", + .ssc = &ssc_reg[1], + .pdc = &pdc_rx_reg[1], + .mask = &ssc_rx_mask, + }}, + {{ + .name = "SSC2/I2S PCM Stereo out", + .ssc = &ssc_reg[2], + .pdc = &pdc_tx_reg[2], + .mask = &ssc_tx_mask, + }, + { + .name = "SSC1/I2S PCM Stereo in", + .ssc = &ssc_reg[2], + .pdc = &pdc_rx_reg[2], + .mask = &ssc_rx_mask, + }}, +}; + + +struct at91rm9200_ssc_state { + u32 ssc_cmr; + u32 ssc_rcmr; + u32 ssc_rfmr; + u32 ssc_tcmr; + u32 ssc_tfmr; + u32 ssc_sr; + u32 ssc_imr; +}; + +static struct at91rm9200_ssc_info { + char *name; + void __iomem *ssc_base; + u32 pid; + spinlock_t lock; /* lock for dir_mask */ + int dir_mask; /* 0=unused, 1=playback, 2=capture */ + struct semaphore *mutex; + int initialized; + int pcmfmt; + int rate; + at91rm9200_pcm_dma_params_t *dma_params[2]; + struct at91rm9200_ssc_state ssc_state; + +} ssc_info[3] = { + { + .name = "ssc0", + .ssc_base = (void __iomem *) AT91_VA_BASE_SSC0, + .pid = AT91_ID_SSC0, + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc0_mutex, + .initialized = 0, + }, + { + .name = "ssc1", + .ssc_base = (void __iomem *) AT91_VA_BASE_SSC1, + .pid = AT91_ID_SSC1, + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc1_mutex, + .initialized = 0, + }, + { + .name = "ssc2", + .ssc_base = (void __iomem *) AT91_VA_BASE_SSC2, + .pid = AT91_ID_SSC2, + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc2_mutex, + .initialized = 0, + }, +}; + + +static int at91rm9200_i2s_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct at91rm9200_ssc_info *ssc_p = dev_id; + at91rm9200_pcm_dma_params_t *dma_params; + u32 ssc_sr; + int i; + + ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR) + & at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); + + /* + * Loop through the substreams attached to this SSC. If + * a DMA-related interrupt occurred on that substream, call + * the DMA interrupt handler function, if one has been + * registered in the dma_params structure by the PCM driver. + */ + for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { + dma_params = ssc_p->dma_params[i]; + + if (dma_params != NULL && dma_params->dma_intr_handler != NULL && + (ssc_sr & + (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf))) + + dma_params->dma_intr_handler(ssc_sr, dma_params->substream); + } + + return IRQ_HANDLED; +} + +static int at91rm9200_i2s_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; + int dir_mask; + + DBG("i2s_startup: SSC_SR=0x%08lx\n", + at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); + dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; + + spin_lock_irq(&ssc_p->lock); + if (ssc_p->dir_mask & dir_mask) { + spin_unlock_irq(&ssc_p->lock); + return -EBUSY; + } + ssc_p->dir_mask |= dir_mask; + spin_unlock_irq(&ssc_p->lock); + + return 0; +} + +static void at91rm9200_i2s_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; + at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; + int dir, dir_mask; + + dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + if (dma_params != NULL) { + at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_disable); + DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"), + at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); + + dma_params->substream = NULL; + ssc_p->dma_params[dir] = NULL; + } + + dir_mask = 1 << dir; + + spin_lock_irq(&ssc_p->lock); + ssc_p->dir_mask &= ~dir_mask; + if (!ssc_p->dir_mask) { + /* Shutdown the SSC clock. */ + DBG("Stopping pid %d clock\n", ssc_p->pid); + at91_sys_write(AT91_PMC_PCDR, ssc_p->pid); + + if (ssc_p->initialized) + free_irq(ssc_p->pid, ssc_p); + + /* Reset the SSC */ + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); + + /* Force a re-init on the next hw_params() call. */ + ssc_p->initialized = 0; + } + spin_unlock_irq(&ssc_p->lock); +} + +#ifdef CONFIG_PM +static int at91rm9200_i2s_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct at91rm9200_ssc_info *ssc_p; + + if(!dai->active) + return 0; + + ssc_p = &ssc_info[dai->id]; + + /* Save the status register before disabling transmit and receive. */ + ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR); + at91_ssc_write(ssc_p->ssc_base + + AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); + + /* Save the current interrupt mask, then disable unmasked interrupts. */ + ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IDR, ssc_p->state->ssc_imr); + + ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_CMR); + ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); + ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); + ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); + ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); + + return 0; +} + +static int at91rm9200_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct at91rm9200_ssc_info *ssc_p; + u32 cr_mask; + + if(!dai->active) + return 0; + + ssc_p = &ssc_info[dai->id]; + + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, ssc_p->state->ssc_cmr); + + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IER, ssc_p->state->ssc_imr); + + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, + ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | + ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); + + return 0; +} + +#else +#define at91rm9200_i2s_suspend NULL +#define at91rm9200_i2s_resume NULL +#endif + +static unsigned int at91rm9200_i2s_config_sysclk( + struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info, + unsigned int clk) +{ + /* Currently, there is only support for USB (12Mhz) mode */ + if (clk != 12000000) + return 0; + return 12000000; +} + +static int at91rm9200_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int id = rtd->cpu_dai->id; + struct at91rm9200_ssc_info *ssc_p = &ssc_info[id]; + at91rm9200_pcm_dma_params_t *dma_params; + unsigned int pcmfmt, rate; + int dir, channels, bits; + struct clk *mck_clk; + unsigned long bclk; + u32 div, period, tfmr, rfmr, tcmr, rcmr; + int ret; + + /* + * Currently, there is only one set of dma params for + * each direction. If more are added, this code will + * have to be changed to select the proper set. + */ + dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + dma_params = &ssc_dma_params[id][dir]; + dma_params->substream = substream; + + ssc_p->dma_params[dir] = dma_params; + rtd->cpu_dai->dma_data = dma_params; + + rate = params_rate(params); + channels = params_channels(params); + + pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt; + switch (pcmfmt) { + case SNDRV_PCM_FMTBIT_S16_LE: + /* likely this is all we'll ever support, but ... */ + bits = 16; + dma_params->pdc_xfer_size = 2; + break; + default: + printk(KERN_WARNING "at91rm9200-i2s: unsupported format %x\n", + pcmfmt); + return -EINVAL; + } + + /* Don't allow both SSC substreams to initialize at the same time. */ + down(ssc_p->mutex); + + /* + * If this SSC is alreadly initialized, then this substream must use + * the same format and rate. + */ + if (ssc_p->initialized) { + if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) { + printk(KERN_WARNING "at91rm9200-i2s: " + "incompatible substream in other direction\n"); + up(ssc_p->mutex); + return -EINVAL; + } + } else { + /* Enable PMC peripheral clock for this SSC */ + DBG("Starting pid %d clock\n", ssc_p->pid); + at91_sys_write(AT91_PMC_PCER, 1<pid); + + /* Reset the SSC */ + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); + + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RPR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RCR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNPR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNCR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TPR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TCR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNPR, 0); + at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNCR, 0); + + mck_clk = clk_get(NULL, "mck"); + + div = rtd->cpu_dai->dai_runtime.priv >> 16; + period = rtd->cpu_dai->dai_runtime.priv & 0xffff; + bclk = 60000000 / (2 * div); + + DBG("mck %ld fsbd %d bfs %d bfs_real %d bclk %ld div %d period %d\n", + clk_get_rate(mck_clk), + SND_SOC_FSBD(6), + rtd->cpu_dai->dai_runtime.bfs, + SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), + bclk, + div, + period); + + clk_put(mck_clk); + + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, div); + + /* + * Setup the TFMR and RFMR for the proper data format. + */ + tfmr = + (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( 0 << 23) & AT91_SSC_FSDEN) + | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) + | (((bits - 1) << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_DATDEF) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + DBG("SSC_TFMR=0x%08x\n", tfmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TFMR, tfmr); + + rfmr = + (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_LOOP) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + DBG("SSC_RFMR=0x%08x\n", rfmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RFMR, rfmr); + + /* + * Setup the TCMR and RCMR to generate the proper BCLK + * and LRC signals. + */ + tcmr = + (( period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + DBG("SSC_TCMR=0x%08x\n", tcmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TCMR, tcmr); + + rcmr = + (( 0 << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); + + DBG("SSC_RCMR=0x%08x\n", rcmr); + at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, rcmr); + + if ((ret = request_irq(ssc_p->pid, at91rm9200_i2s_interrupt, + 0, ssc_p->name, ssc_p)) < 0) { + printk(KERN_WARNING "at91rm9200-i2s: request_irq failure\n"); + return ret; + } + + /* + * Save the current substream parameters in order to check + * that the substream in the opposite direction uses the + * same parameters. + */ + ssc_p->pcmfmt = pcmfmt; + ssc_p->rate = rate; + ssc_p->initialized = 1; + + DBG("hw_params: SSC initialized\n"); + } + + up(ssc_p->mutex); + + return 0; +} + + +static int at91rm9200_i2s_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; + + at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_enable); + + DBG("%s enabled SSC_SR=0x%08lx\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive", + at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc_base + AT91_SSC_SR)); + return 0; +} + + +struct snd_soc_cpu_dai at91rm9200_i2s_dai[] = { + { .name = "at91rm9200-ssc0/i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .suspend = at91rm9200_i2s_suspend, + .resume = at91rm9200_i2s_resume, + .config_sysclk = at91rm9200_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91rm9200_i2s_startup, + .shutdown = at91rm9200_i2s_shutdown, + .prepare = at91rm9200_i2s_prepare, + .hw_params = at91rm9200_i2s_hw_params,}, + .caps = { + .mode = &at91rm9200_i2s[0], + .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, + }, + { .name = "at91rm9200-ssc1/i2s", + .id = 1, + .type = SND_SOC_DAI_I2S, + .suspend = at91rm9200_i2s_suspend, + .resume = at91rm9200_i2s_resume, + .config_sysclk = at91rm9200_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91rm9200_i2s_startup, + .shutdown = at91rm9200_i2s_shutdown, + .prepare = at91rm9200_i2s_prepare, + .hw_params = at91rm9200_i2s_hw_params,}, + .caps = { + .mode = &at91rm9200_i2s[0], + .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, + }, + { .name = "at91rm9200-ssc2/i2s", + .id = 2, + .type = SND_SOC_DAI_I2S, + .suspend = at91rm9200_i2s_suspend, + .resume = at91rm9200_i2s_resume, + .config_sysclk = at91rm9200_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91rm9200_i2s_startup, + .shutdown = at91rm9200_i2s_shutdown, + .prepare = at91rm9200_i2s_prepare, + .hw_params = at91rm9200_i2s_hw_params,}, + .caps = { + .mode = &at91rm9200_i2s[0], + .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, + }, +}; + +EXPORT_SYMBOL_GPL(at91rm9200_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); +MODULE_DESCRIPTION("AT91RM9200 I2S ASoC Interface"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From b41bf38a4323a32ec4890c74818c4a3d2661fe6c Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 6 Oct 2006 18:41:10 +0200 Subject: [ALSA] ASoC AT91RM92000 eti_b1 machine support This patch adds support for the Endrelia ETI_B1 machine using the WM8731 codec and the AT91RM9200 platform. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/eti_b1_wm8731.c | 230 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 sound/soc/at91/eti_b1_wm8731.c diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c new file mode 100644 index 000000000000..d955cacf2d0d --- /dev/null +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -0,0 +1,230 @@ +/* + * eti_b1_wm8731 -- SoC audio for Endrelia ETI_B1. + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Mar 29, 2006 + * + * Based on corgi.c by: + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * Authors: Liam Girdwood + * Richard Purdie + * + * 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. + * + * Revision history + * 30th Nov 2005 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../codecs/wm8731.h" +#include "at91rm9200-pcm.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731:" x) +#else +#define DBG(x...) +#endif + +static struct clk *pck1_clk; +static struct clk *pllb_clk; + +static int eti_b1_startup(snd_pcm_substream_t *substream) +{ + /* Start PCK1 clock. */ + clk_enable(pck1_clk); + DBG("pck1 started\n"); + + return 0; +} + +static void eti_b1_shutdown(snd_pcm_substream_t *substream) +{ + /* Stop PCK1 clock. */ + clk_disable(pck1_clk); + DBG("pck1 stopped\n"); +} + +static struct snd_soc_ops eti_b1_ops = { + .startup = eti_b1_startup, + .shutdown = eti_b1_shutdown, +}; + + +static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const char *intercon[][3] = { + + /* speaker connected to LHPOUT */ + {"Ext Spk", NULL, "LHPOUT"}, + + /* mic is connected to Mic Jack, with WM8731 Mic Bias */ + {"MICIN", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Int Mic"}, + + /* terminator */ + {NULL, NULL, NULL}, +}; + +/* + * Logic for a wm8731 as connected on a Endrelia ETI-B1 board. + */ +static int eti_b1_wm8731_init(struct snd_soc_codec *codec) +{ + int i; + + DBG("eti_b1_wm8731_init() called\n"); + + /* Add specific widgets */ + for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]); + } + + /* Set up specific audio path interconnects */ + for(i = 0; intercon[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, intercon[i][0], + intercon[i][1], intercon[i][2]); + } + + /* not connected */ + snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); + + /* always connected */ + snd_soc_dapm_set_endpoint(codec, "Int Mic", 1); + snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); + + snd_soc_dapm_sync_endpoints(codec); + + return 0; +} + +unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) +{ + if(info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12000000); + } + return 0; +} + +static struct snd_soc_dai_link eti_b1_dai = { + .name = "WM8731", + .stream_name = "WM8731", + .cpu_dai = &at91rm9200_i2s_dai[1], + .codec_dai = &wm8731_dai, + .init = eti_b1_wm8731_init, + .config_sysclk = eti_b1_config_sysclk, +}; + +static struct snd_soc_machine snd_soc_machine_eti_b1 = { + .name = "ETI_B1", + .dai_link = &eti_b1_dai, + .num_links = 1, + .ops = &eti_b1_ops, +}; + +static struct wm8731_setup_data eti_b1_wm8731_setup = { + .i2c_address = 0x1a, +}; + +static struct snd_soc_device eti_b1_snd_devdata = { + .machine = &snd_soc_machine_eti_b1, + .platform = &at91rm9200_soc_platform, + .codec_dev = &soc_codec_dev_wm8731, + .codec_data = &eti_b1_wm8731_setup, +}; + +static struct platform_device *eti_b1_snd_device; + +static int __init eti_b1_init(void) +{ + int ret; + u32 ssc_pio_lines; + + eti_b1_snd_device = platform_device_alloc("soc-audio", -1); + if (!eti_b1_snd_device) + return -ENOMEM; + + platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata); + eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev; + + ret = platform_device_add(eti_b1_snd_device); + if (ret) { + platform_device_put(eti_b1_snd_device); + return ret; + } + + ssc_pio_lines = AT91_PB6_TF1 | AT91_PB7_TK1 | AT91_PB8_TD1 + | AT91_PB9_RD1 /* | AT91_PB10_RK1 | AT91_PB11_RF1 */; + + /* Reset all PIO registers and assign lines to peripheral A */ + at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_ODR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_IFDR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_CODR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_IDR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_MDDR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_PUDR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_ASR, ssc_pio_lines); + at91_sys_write(AT91_PIOB + PIO_OWDR, ssc_pio_lines); + + /* + * Set PCK1 parent to PLLB and its rate to 12 Mhz. + */ + pllb_clk = clk_get(NULL, "pllb"); + pck1_clk = clk_get(NULL, "pck1"); + + clk_set_parent(pck1_clk, pllb_clk); + clk_set_rate(pck1_clk, 12000000); + + DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk)); + + /* assign the GPIO pin to PCK1 */ + at91_set_B_periph(AT91_PIN_PA24, 0); + + return ret; +} + +static void __exit eti_b1_exit(void) +{ + clk_put(pck1_clk); + clk_put(pllb_clk); + + platform_device_unregister(eti_b1_snd_device); +} + +module_init(eti_b1_init); +module_exit(eti_b1_exit); + +/* Module information */ +MODULE_AUTHOR("Frank Mandarino "); +MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 8dafc0fb49b903c4e7262b2622bef8342345c700 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 6 Oct 2006 18:41:42 +0200 Subject: [ALSA] ASoC AT91RM92000 build This patch adds a Makefile and Kconfig to build the ASoC AT91RM9200 support. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/Kconfig | 6 ++++++ sound/soc/Makefile | 2 +- sound/soc/at91/Kconfig | 24 ++++++++++++++++++++++++ sound/soc/at91/Makefile | 11 +++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 sound/soc/at91/Kconfig create mode 100644 sound/soc/at91/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 288bad30b213..e7dab5bdc6b2 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -19,6 +19,12 @@ config SND_SOC This SoC audio support can also be built as a module. If so, the module will be called snd-soc-core. +# All the supported Soc's +menu "SoC Platforms" +depends on SND_SOC +source "sound/soc/at91/Kconfig" +endmenu + # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 3dd4f20c5ecf..3e12a1654c49 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig new file mode 100644 index 000000000000..d38ba9203bd9 --- /dev/null +++ b/sound/soc/at91/Kconfig @@ -0,0 +1,24 @@ +menu "SoC Audio for the Atmel AT91" + +config SND_AT91_SOC + tristate "SoC Audio for the Atmel AT91 System-on-Chip" + depends on ARCH_AT91 && SND + select SND_PCM + help + Say Y or M if you want to add support for codecs attached to + the AT91 SSC interface. You will also need + to select the audio interfaces to support below. + +config SND_AT91_SOC_I2S + tristate + +config SND_AT91_SOC_ETI_B1_WM8731 + tristate "SoC I2S Audio support for Endrelia ETI-B1 board" + depends on SND_AT91_SOC && MACH_ETI_B1 + select SND_AT91_SOC_I2S + select SND_SOC_WM8731 + help + Say Y if you want to add support for SoC audio on Endrelia + ETI-B1 board. + +endmenu diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile new file mode 100644 index 000000000000..eb12ea2d1944 --- /dev/null +++ b/sound/soc/at91/Makefile @@ -0,0 +1,11 @@ +# AT91 Platform Support +snd-soc-at91-objs := at91rm9200-pcm.o +snd-soc-at91-i2s-objs := at91rm9200-i2s.o + +obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o +obj-$(CONFIG_SND_AT91_SOC_I2S) += snd-soc-at91-i2s.o + +# AT91 Machine Support +snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o + +obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o -- cgit v1.2.3-59-g8ed1b From e117483e3e713c6411968afea825daa1133bc28d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 9 Oct 2006 08:14:58 +0200 Subject: [ALSA] soc-core: fix multi-line string literal Properly quote a string that had an embedded newline. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e841ad46c759..eba0a1012531 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -575,8 +575,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name); - dbg("asoc: rate mask 0x%x \nasoc: min ch %d max ch %d\n - asoc: min rate %d max rate %d\n", + dbg("asoc: rate mask 0x%x \nasoc: min ch %d max ch %d\n" + "asoc: min rate %d max rate %d\n", runtime->hw.rates, runtime->hw.channels_min, runtime->hw.channels_max, runtime->hw.rate_min, runtime->hw.rate_max); -- cgit v1.2.3-59-g8ed1b From 8a89876bc108cacebbe5cc47049c162a8a143b26 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 9 Oct 2006 08:17:48 +0200 Subject: [ALSA] pci: select FW_LOADER instead of depending on it Let the AudioScience, Echoaudio and Riptide drivers select FW_LOADER instead of depending on it so that they can be configured without having to enable FW_LOADER manually. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/Kconfig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 8a6b1803c763..ae82024f87c0 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -236,7 +236,7 @@ config SND_CS5535AUDIO config SND_DARLA20 tristate "(Echoaudio) Darla20" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Darla. @@ -247,7 +247,7 @@ config SND_DARLA20 config SND_GINA20 tristate "(Echoaudio) Gina20" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Gina. @@ -258,7 +258,7 @@ config SND_GINA20 config SND_LAYLA20 tristate "(Echoaudio) Layla20" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_RAWMIDI select SND_PCM help @@ -270,7 +270,7 @@ config SND_LAYLA20 config SND_DARLA24 tristate "(Echoaudio) Darla24" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Darla24. @@ -281,7 +281,7 @@ config SND_DARLA24 config SND_GINA24 tristate "(Echoaudio) Gina24" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Gina24. @@ -292,7 +292,7 @@ config SND_GINA24 config SND_LAYLA24 tristate "(Echoaudio) Layla24" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_RAWMIDI select SND_PCM help @@ -304,7 +304,7 @@ config SND_LAYLA24 config SND_MONA tristate "(Echoaudio) Mona" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_RAWMIDI select SND_PCM help @@ -316,7 +316,7 @@ config SND_MONA config SND_MIA tristate "(Echoaudio) Mia" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_RAWMIDI select SND_PCM help @@ -328,7 +328,7 @@ config SND_MIA config SND_ECHO3G tristate "(Echoaudio) 3G cards" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_RAWMIDI select SND_PCM help @@ -340,7 +340,7 @@ config SND_ECHO3G config SND_INDIGO tristate "(Echoaudio) Indigo" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Indigo. @@ -351,7 +351,7 @@ config SND_INDIGO config SND_INDIGOIO tristate "(Echoaudio) Indigo IO" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Indigo IO. @@ -362,7 +362,7 @@ config SND_INDIGOIO config SND_INDIGODJ tristate "(Echoaudio) Indigo DJ" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_PCM help Say 'Y' or 'M' to include support for Echoaudio Indigo DJ. @@ -629,7 +629,7 @@ config SND_PCXHR config SND_RIPTIDE tristate "Conexant Riptide" depends on SND - depends on FW_LOADER + select FW_LOADER select SND_OPL3_LIB select SND_MPU401_UART select SND_AC97_CODEC -- cgit v1.2.3-59-g8ed1b From 9bf5f8aa222e0f943bd5037207628ad70b729576 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 9 Oct 2006 08:18:26 +0200 Subject: [ALSA] emu10k1: select FW_LOADER Let the emu10k1 driver select FW_LOADER because the new Emu1010 support requires it. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index ae82024f87c0..ee37de940c63 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -373,6 +373,7 @@ config SND_INDIGODJ config SND_EMU10K1 tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)" depends on SND + select FW_LOADER select SND_HWDEP select SND_RAWMIDI select SND_AC97_CODEC -- cgit v1.2.3-59-g8ed1b From 6add0f4242fc52a97a92fca99a39f35298c2b50b Mon Sep 17 00:00:00 2001 From: Remy Bruno Date: Mon, 9 Oct 2006 15:52:01 +0200 Subject: [ALSA] hdsp: support for mixer matrix of RME9632 rev 152 Added the support for mixer matrix of RME9632 rev 152. Signed-off-by: Remy Bruno Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/rme9652/hdsp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 6383987b460e..849ffe4aa5ca 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -598,6 +598,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out) return (64 * out) + (32 + (in)); case 0x96: case 0x97: + case 0x98: return (32 * out) + (16 + (in)); default: return (52 * out) + (26 + (in)); @@ -611,6 +612,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out) return (64 * out) + in; case 0x96: case 0x97: + case 0x98: return (32 * out) + in; default: return (52 * out) + in; -- cgit v1.2.3-59-g8ed1b From 9148cc502752b12051760e6c5ba5daaea3367360 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Mon, 9 Oct 2006 23:08:00 +0100 Subject: [ALSA] snd_emu10k1: Added support for 14dB Attenuation PADS on DACs and ADCs. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 29 ++++-- sound/pci/emu10k1/emu10k1_main.c | 29 +++--- sound/pci/emu10k1/emumixer.c | 216 ++++++++++++++++++++++++++++++--------- 3 files changed, 209 insertions(+), 65 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 396812eb668d..7cfa91e7bde8 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -974,7 +974,7 @@ #define EMU_HANA_OPTICAL_OUT_SPDIF 0x00 #define EMU_HANA_OPTICAL_OUT_ADAT 0x02 -#define EMU_HANA_MIDI 0x0c /* 000000x 1 bit Control MIDI */ +#define EMU_HANA_MIDI_IN 0x0c /* 000000x 1 bit Control MIDI */ #define EMU_HANA_MIDI_IN_FROM_HAMOA 0x00 /* HAMOA MIDI in to Alice 2 MIDI B */ #define EMU_HANA_MIDI_IN_FROM_DOCK 0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */ @@ -1000,10 +1000,11 @@ #define EMU_HANA_DOCK_LEDS_3_MANUAL_CLIP 0x10 /* Manual Clip detection */ #define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL 0x20 /* Manual Signal detection */ -#define EMU_HANA_DOCK_PADS 0x10 /* 0000xxx 3 bit Audio Dock ADC 14dB pads */ -#define EMU_HANA_DOCK_PAD1 0x01 /* 14dB Attenuation on ADC 1 */ -#define EMU_HANA_DOCK_PAD2 0x02 /* 14dB Attenuation on ADC 2 */ -#define EMU_HANA_DOCK_PAD3 0x04 /* 14dB Attenuation on ADC 3 */ +#define EMU_HANA_ADC_PADS 0x10 /* 0000xxx 3 bit Audio Dock ADC 14dB pads */ +#define EMU_HANA_DOCK_ADC_PAD1 0x01 /* 14dB Attenuation on Audio Dock ADC 1 */ +#define EMU_HANA_DOCK_ADC_PAD2 0x02 /* 14dB Attenuation on Audio Dock ADC 2 */ +#define EMU_HANA_DOCK_ADC_PAD3 0x04 /* 14dB Attenuation on Audio Dock ADC 3 */ +#define EMU_HANA_0202_ADC_PAD1 0x08 /* 14dB Attenuation on 0202 ADC 1 */ #define EMU_HANA_DOCK_MISC 0x11 /* 0xxxxxx 6 bit Audio Dock misc bits */ #define EMU_HANA_DOCK_DAC1_MUTE 0x01 /* DAC 1 Mute */ @@ -1015,8 +1016,20 @@ #define EMU_HANA_DOCK_PHONES_192_DAC3 0x20 /* DAC 3 Headphones source at 192kHz */ #define EMU_HANA_DOCK_PHONES_192_DAC4 0x30 /* DAC 4 Headphones source at 192kHz */ -#define EMU_HANA_UNKNOWN12 0x12 /* 0xxxxxx 6 bit Unknown12 */ -#define EMU_HANA_UNKNOWN13 0x13 /* 0xxxxxx 6 bit Unknown13 */ +#define EMU_HANA_MIDI_OUT 0x12 /* 00xxxxx 5 bit Source for each MIDI out port */ +#define EMU_HANA_MIDI_OUT_0202 0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_DOCK1 0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_DOCK2 0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_SYNC2 0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_LOOP 0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */ + +#define EMU_HANA_DAC_PADS 0x13 /* 00xxxxx 5 bit DAC 14dB attenuation pads */ +#define EMU_HANA_DOCK_DAC_PAD1 0x01 /* 14dB Attenuation on AudioDock DAC 1. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD2 0x02 /* 14dB Attenuation on AudioDock DAC 2. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD3 0x04 /* 14dB Attenuation on AudioDock DAC 3. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD4 0x08 /* 14dB Attenuation on AudioDock DAC 4. Left and Right */ +#define EMU_HANA_0202_DAC_PAD1 0x10 /* 14dB Attenuation on 0202 DAC 1. Left and Right */ + /* 0x14 - 0x1f Unused R/W registers */ #define EMU_HANA_IRQ_STATUS 0x20 /* 000xxxx 4 bits IRQ Status */ #if 0 /* Already defined for reg 0x09 IRQ_ENABLE */ @@ -1377,6 +1390,8 @@ struct snd_emu_chip_details { struct snd_emu1010 { unsigned int output_source[64]; unsigned int input_source[64]; + unsigned int adc_pads; /* bit mask */ + unsigned int dac_pads; /* bit mask */ }; struct snd_emu10k1 { diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 09c4db8495b2..341a2775d286 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -725,25 +725,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); /* ADAT input. */ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 ); - snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_PADS, &tmp ); + snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp ); /* Set no attenuation on Audio Dock pads. */ - snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PADS, 0x00 ); + snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 ); + emu->emu1010.adc_pads = 0x00; snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); /* Unmute Audio dock DACs, Headphone source DAC-4. */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 ); - snd_emu1010_fpga_read(emu, EMU_HANA_UNKNOWN13, &tmp ); - /* Unknown. */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN13, 0x0f ); + snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp ); + /* DAC PADs. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f ); + emu->emu1010.dac_pads = 0x0f; snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); /* SPDIF Format. Set Consumer mode, 24bit, copy enable */ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* MIDI routing */ - snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* Unknown. */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */ /* IRQ Enable: All off */ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 ); @@ -880,10 +882,10 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) /* Initial boot complete. Now patches */ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp ); - snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); /* MIDI Route */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); /* Unknown */ - snd_emu1010_fpga_write(emu, EMU_HANA_MIDI, 0x19 ); /* MIDI Route */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNKNOWN12, 0x0c ); /* Unknown */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ @@ -902,7 +904,6 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) if ((err = snd_emu1010_load_firmware(emu, dock_filename)) != 0) { return err; } - snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® ); snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); @@ -915,6 +916,10 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) return 0; return -ENODEV; } + snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 ); + snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2); } #if 0 snd_emu1010_fpga_link_dst_src_write(emu, diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index c8176dc8142f..2edf92a4f065 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -315,30 +315,30 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { - EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Left", 0), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Right", 1), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Left", 2), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Right", 3), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Left", 4), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Right", 5), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Left", 6), - EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Right", 7), - EMU1010_SOURCE_OUTPUT("Playback Dock Phones Left", 8), - EMU1010_SOURCE_OUTPUT("Playback Dock Phones Right", 9), - EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Left", 0xa), - EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Right", 0xb), - EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Left", 0xc), - EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Right", 0xd), - EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Left", 0xe), - EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Right", 0xf), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 0", 0x10), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 1", 0x11), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 2", 0x12), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 3", 0x13), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 4", 0x14), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 5", 0x15), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 6", 0x16), - EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 7", 0x17), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Switch", 0), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Switch", 1), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Switch", 2), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Switch", 3), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Switch", 4), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Switch", 5), + EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Switch", 6), + EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Switch", 7), + EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Switch", 8), + EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Switch", 9), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Switch", 0xa), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Switch", 0xb), + EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Switch", 0xc), + EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Switch", 0xd), + EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Switch", 0xe), + EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Switch", 0xf), + EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Switch", 0x10), + EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Switch", 0x11), + EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Switch", 0x12), + EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Switch", 0x13), + EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Switch", 0x14), + EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Switch", 0x15), + EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Switch", 0x16), + EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Switch", 0x17), }; #define EMU1010_SOURCE_INPUT(xname,chid) \ @@ -352,28 +352,142 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { } static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = { - EMU1010_SOURCE_INPUT("DSP 0 CAPTURE ENUM", 0), - EMU1010_SOURCE_INPUT("DSP 1 CAPTURE ENUM", 1), - EMU1010_SOURCE_INPUT("DSP 2 CAPTURE ENUM", 2), - EMU1010_SOURCE_INPUT("DSP 3 CAPTURE ENUM", 3), - EMU1010_SOURCE_INPUT("DSP 4 CAPTURE ENUM", 4), - EMU1010_SOURCE_INPUT("DSP 5 CAPTURE ENUM", 5), - EMU1010_SOURCE_INPUT("DSP 6 CAPTURE ENUM", 6), - EMU1010_SOURCE_INPUT("DSP 7 CAPTURE ENUM", 7), - EMU1010_SOURCE_INPUT("DSP 8 CAPTURE ENUM", 8), - EMU1010_SOURCE_INPUT("DSP 9 CAPTURE ENUM", 9), - EMU1010_SOURCE_INPUT("DSP A CAPTURE ENUM", 0xa), - EMU1010_SOURCE_INPUT("DSP B CAPTURE ENUM", 0xb), - EMU1010_SOURCE_INPUT("DSP C CAPTURE ENUM", 0xc), - EMU1010_SOURCE_INPUT("DSP D CAPTURE ENUM", 0xd), - EMU1010_SOURCE_INPUT("DSP E CAPTURE ENUM", 0xe), - EMU1010_SOURCE_INPUT("DSP F CAPTURE ENUM", 0xf), - EMU1010_SOURCE_INPUT("DSP 10 CAPTURE ENUM", 0x10), - EMU1010_SOURCE_INPUT("DSP 11 CAPTURE ENUM", 0x11), - EMU1010_SOURCE_INPUT("DSP 12 CAPTURE ENUM", 0x12), - EMU1010_SOURCE_INPUT("DSP 13 CAPTURE ENUM", 0x13), - EMU1010_SOURCE_INPUT("DSP 14 CAPTURE ENUM", 0x14), - EMU1010_SOURCE_INPUT("DSP 15 CAPTURE ENUM", 0x15), + EMU1010_SOURCE_INPUT("DSP 0 Capture Switch", 0), + EMU1010_SOURCE_INPUT("DSP 1 Capture Switch", 1), + EMU1010_SOURCE_INPUT("DSP 2 Capture Switch", 2), + EMU1010_SOURCE_INPUT("DSP 3 Capture Switch", 3), + EMU1010_SOURCE_INPUT("DSP 4 Capture Switch", 4), + EMU1010_SOURCE_INPUT("DSP 5 Capture Switch", 5), + EMU1010_SOURCE_INPUT("DSP 6 Capture Switch", 6), + EMU1010_SOURCE_INPUT("DSP 7 Capture Switch", 7), + EMU1010_SOURCE_INPUT("DSP 8 Capture Switch", 8), + EMU1010_SOURCE_INPUT("DSP 9 Capture Switch", 9), + EMU1010_SOURCE_INPUT("DSP A Capture Switch", 0xa), + EMU1010_SOURCE_INPUT("DSP B Capture Switch", 0xb), + EMU1010_SOURCE_INPUT("DSP C Capture Switch", 0xc), + EMU1010_SOURCE_INPUT("DSP D Capture Switch", 0xd), + EMU1010_SOURCE_INPUT("DSP E Capture Switch", 0xe), + EMU1010_SOURCE_INPUT("DSP F Capture Switch", 0xf), + EMU1010_SOURCE_INPUT("DSP 10 Capture Switch", 0x10), + EMU1010_SOURCE_INPUT("DSP 11 Capture Switch", 0x11), + EMU1010_SOURCE_INPUT("DSP 12 Capture Switch", 0x12), + EMU1010_SOURCE_INPUT("DSP 13 Capture Switch", 0x13), + EMU1010_SOURCE_INPUT("DSP 14 Capture Switch", 0x14), + EMU1010_SOURCE_INPUT("DSP 15 Capture Switch", 0x15), +}; + + + + +static int snd_emu1010_adc_pads_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 snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = kcontrol->private_value & 0xff; + ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0; + return 0; +} + +static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = kcontrol->private_value & 0xff; + unsigned int val, cache; + val = ucontrol->value.integer.value[0]; + cache = emu->emu1010.adc_pads; + if (val == 1) + cache = cache | mask; + else + cache = cache & ~mask; + if (cache != emu->emu1010.adc_pads) { + snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache ); + emu->emu1010.adc_pads = cache; + } + + return 0; +} + + + +#define EMU1010_ADC_PADS(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_emu1010_adc_pads_info, \ + .get = snd_emu1010_adc_pads_get, \ + .put = snd_emu1010_adc_pads_put, \ + .private_value = chid \ +} + +static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = { + EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1), + EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2), + EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3), + EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1), +}; + +static int snd_emu1010_dac_pads_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 snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = kcontrol->private_value & 0xff; + ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0; + return 0; +} + +static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = kcontrol->private_value & 0xff; + unsigned int val, cache; + val = ucontrol->value.integer.value[0]; + cache = emu->emu1010.dac_pads; + if (val == 1) + cache = cache | mask; + else + cache = cache & ~mask; + if (cache != emu->emu1010.dac_pads) { + snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache ); + emu->emu1010.dac_pads = cache; + } + + return 0; +} + + + +#define EMU1010_DAC_PADS(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_emu1010_dac_pads_info, \ + .get = snd_emu1010_dac_pads_get, \ + .put = snd_emu1010_dac_pads_put, \ + .private_value = chid \ +} + +static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = { + EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1), + EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2), + EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3), + EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4), + EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1), }; #if 0 @@ -1367,6 +1481,16 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if (err < 0) return err; } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); + if (err < 0) + return err; + } } return 0; -- cgit v1.2.3-59-g8ed1b From 0f71e8b98506252db22a0c4fcfecb0aadcf393cc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Oct 2006 15:59:46 +0200 Subject: [ALSA] Fix irq handler in soc/at91/at91rm9200-i2s.c Fixed the irq handler in soc/at91-at91rm9200-i2s.c to follow the new style without pt_regs. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c index a74c5d85589b..044a774b4301 100644 --- a/sound/soc/at91/at91rm9200-i2s.c +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -270,8 +270,7 @@ static struct at91rm9200_ssc_info { }; -static int at91rm9200_i2s_interrupt(int irq, void *dev_id, - struct pt_regs *regs) +static irqreturn_t at91rm9200_i2s_interrupt(int irq, void *dev_id) { struct at91rm9200_ssc_info *ssc_p = dev_id; at91rm9200_pcm_dma_params_t *dma_params; -- cgit v1.2.3-59-g8ed1b From b0dbdaea55d55c05be972cd2a040acfa073b0509 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Tue, 10 Oct 2006 18:08:45 +0100 Subject: [ALSA] snd-emu10k1: Add emu1010 internal clock rate control for 44100 or 48000. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 1 + sound/pci/emu10k1/emu10k1_main.c | 1 + sound/pci/emu10k1/emumixer.c | 92 ++++++++++++++++++++++++++++++++++++++++ sound/pci/emu10k1/emupcm.c | 28 +++++++++--- 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 7cfa91e7bde8..8b28304f2c42 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1392,6 +1392,7 @@ struct snd_emu1010 { unsigned int input_source[64]; unsigned int adc_pads; /* bit mask */ unsigned int dac_pads; /* bit mask */ + unsigned int internal_clock; /* 44100 or 48000 */ }; struct snd_emu10k1 { diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 341a2775d286..711e819e4a0b 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1014,6 +1014,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) /* Word Clock source, Internal 48kHz x1 */ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K ); //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X ); + emu->emu1010.internal_clock = 1; /* 48000 */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */ //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */ diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 2edf92a4f065..a118fee11cdd 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -35,6 +35,7 @@ #include #include #include +#include #define AC97_ID_STAC9758 0x83847658 @@ -490,6 +491,94 @@ static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = { EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1), }; + +static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2] = { + "44100", "48000" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.internal_clock; + return 0; +} + +static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.enumerated.item[0] ; + change = (emu->emu1010.internal_clock != val); + if (change) { + emu->emu1010.internal_clock = val; + switch (val) { + case 0: + /* 44100 */ + /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); + /* Default fallback clock 48kHz */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K ); + /* Word Clock source, Internal 44.1kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, + EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X ); + /* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, + EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK ); + /* Allow DLL to settle */ + udelay(10000); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + break; + case 1: + /* 48000 */ + /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); + /* Default fallback clock 48kHz */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K ); + /* Word Clock source, Internal 48kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, + EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X ); + /* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, + EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK ); + /* Allow DLL to settle */ + udelay(10000); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + break; + } + } + return change; +} + +static struct snd_kcontrol_new snd_emu1010_internal_clock = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Internal Rate", + .count = 1, + .info = snd_emu1010_internal_clock_info, + .get = snd_emu1010_internal_clock_get, + .put = snd_emu1010_internal_clock_put +}; + #if 0 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1491,6 +1580,9 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if (err < 0) return err; } + err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); + if (err < 0) + return err; } return 0; diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 44d098ac86d5..ab4f5df5241b 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -358,7 +358,10 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); - pitch_target = emu10k1_calc_pitch_target(runtime->rate); + if (emu->card_capabilities->emu1010) + pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ + else + pitch_target = emu10k1_calc_pitch_target(runtime->rate); if (extra) snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr | emu10k1_select_interprom(pitch_target) | @@ -698,7 +701,10 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s voice = evoice->number; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; - pitch_target = emu10k1_calc_pitch_target(runtime->rate); + if (emu->card_capabilities->emu1010) + pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ + else + pitch_target = emu10k1_calc_pitch_target(runtime->rate); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); if (master || evoice->epcm->type == PLAYBACK_EFX) snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); @@ -1247,10 +1253,20 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) * for 192kHz 24bit, one has 2 channels */ #if 1 - /* For 48kHz */ - runtime->hw.rates = SNDRV_PCM_RATE_48000; - runtime->hw.rate_min = runtime->hw.rate_max = 48000; - runtime->hw.channels_min = runtime->hw.channels_max = 8; + switch (emu->emu1010.internal_clock) { + case 0: + /* For 44.1kHz */ + runtime->hw.rates = SNDRV_PCM_RATE_44100; + runtime->hw.rate_min = runtime->hw.rate_max = 44100; + runtime->hw.channels_min = runtime->hw.channels_max = 8; + break; + case 1: + /* For 48kHz */ + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + runtime->hw.channels_min = runtime->hw.channels_max = 8; + break; + }; #endif #if 0 /* For 96kHz */ -- cgit v1.2.3-59-g8ed1b From e40a0b2e9d73c69e6b9e5d55eb56696f81fbf802 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Tue, 10 Oct 2006 18:44:29 +0100 Subject: [ALSA] snd-emu10k1: emu1010: replace long udelay with msleep. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- sound/pci/emu10k1/emumixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index a118fee11cdd..5ceb8dd5cb38 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -542,7 +542,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol, snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK ); /* Allow DLL to settle */ - udelay(10000); + msleep(10); /* Unmute all */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); break; @@ -559,7 +559,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol, snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK ); /* Allow DLL to settle */ - udelay(10000); + msleep(10); /* Unmute all */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); break; -- cgit v1.2.3-59-g8ed1b From 102fa9060e114a53628a6594034b6ecf624dffc6 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 Oct 2006 12:05:59 +0200 Subject: [ALSA] ymfpci: add request_firmware() Load the DSP and controller microcode using request_firmware(), if possible, instead of using the built-in firmware. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- include/sound/ymfpci.h | 2 + sound/pci/Kconfig | 1 + sound/pci/ymfpci/ymfpci_image.h | 6 +- sound/pci/ymfpci/ymfpci_main.c | 125 ++++++++++++++++++++++++++++++++-------- 4 files changed, 108 insertions(+), 26 deletions(-) diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h index f3514ee96bd9..eef46faaee30 100644 --- a/include/sound/ymfpci.h +++ b/include/sound/ymfpci.h @@ -357,6 +357,8 @@ struct snd_ymfpci { wait_queue_head_t interrupt_sleep; atomic_t interrupt_sleep_count; struct snd_info_entry *proc_entry; + const struct firmware *dsp_microcode; + const struct firmware *controller_microcode; #ifdef CONFIG_PM u32 *saved_regs; diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index ee37de940c63..fcbf9673db62 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -735,6 +735,7 @@ config SND_VX222 config SND_YMFPCI tristate "Yamaha YMF724/740/744/754" depends on SND + select FW_LOADER select SND_OPL3_LIB select SND_MPU401_UART select SND_AC97_CODEC diff --git a/sound/pci/ymfpci/ymfpci_image.h b/sound/pci/ymfpci/ymfpci_image.h index 1b0746991669..112f2fff6c8e 100644 --- a/sound/pci/ymfpci/ymfpci_image.h +++ b/sound/pci/ymfpci/ymfpci_image.h @@ -1,7 +1,7 @@ #ifndef _HWMCODE_ #define _HWMCODE_ -static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = { +static u32 DspInst[YDSXG_DSPLENGTH / 4] = { 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, @@ -12,7 +12,7 @@ static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; -static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = { +static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, @@ -791,7 +791,7 @@ static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = { // 04/09 creat // 04/12 stop nise fix // 06/21 WorkingOff timming -static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { +static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 7881944a1957..5bde816cd5c4 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2,12 +2,6 @@ * Copyright (c) by Jaroslav Kysela * Routines for control of YMF724/740/744/754 chips * - * BUGS: - * -- - * - * TODO: - * -- - * * 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 @@ -26,6 +20,7 @@ #include #include +#include #include #include #include @@ -42,10 +37,7 @@ #include #include - -/* - * constants - */ +#include /* * common I/O routines @@ -1971,13 +1963,94 @@ static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip) } } +#define FIRMWARE_IN_THE_KERNEL + +#ifdef FIRMWARE_IN_THE_KERNEL + #include "ymfpci_image.h" +static struct firmware snd_ymfpci_dsp_microcode = { + .size = YDSXG_DSPLENGTH, + .data = (u8 *)DspInst, +}; +static struct firmware snd_ymfpci_controller_microcode = { + .size = YDSXG_CTRLLENGTH, + .data = (u8 *)CntrlInst, +}; +static struct firmware snd_ymfpci_controller_1e_microcode = { + .size = YDSXG_CTRLLENGTH, + .data = (u8 *)CntrlInst1E, +}; +#endif + +#ifdef __LITTLE_ENDIAN +static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { } +#else +static void snd_ymfpci_convert_from_le(const struct firmware *fw) +{ + int i; + u32 *data = (u32 *)fw->data; + + for (i = 0; i < fw->size / 4; ++i) + le32_to_cpus(&data[i]); +} +#endif + +static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip) +{ + int err, is_1e; + const char *name; + + err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw", + &chip->pci->dev); + if (err >= 0) { + if (chip->dsp_microcode->size == YDSXG_DSPLENGTH) + snd_ymfpci_convert_from_le(chip->dsp_microcode); + else { + snd_printk(KERN_ERR "DSP microcode has wrong size\n"); + err = -EINVAL; + } + } + if (err < 0) { +#ifdef FIRMWARE_IN_THE_KERNEL + chip->dsp_microcode = &snd_ymfpci_dsp_microcode; +#else + return err; +#endif + } + is_1e = chip->device_id == PCI_DEVICE_ID_YAMAHA_724F || + chip->device_id == PCI_DEVICE_ID_YAMAHA_740C || + chip->device_id == PCI_DEVICE_ID_YAMAHA_744 || + chip->device_id == PCI_DEVICE_ID_YAMAHA_754; + name = is_1e ? "yamaha/ds1e_ctrl.fw" : "yamaha/ds1_ctrl.fw"; + err = request_firmware(&chip->controller_microcode, name, + &chip->pci->dev); + if (err >= 0) { + if (chip->controller_microcode->size == YDSXG_CTRLLENGTH) + snd_ymfpci_convert_from_le(chip->controller_microcode); + else { + snd_printk(KERN_ERR "controller microcode" + " has wrong size\n"); + err = -EINVAL; + } + } + if (err < 0) { +#ifdef FIRMWARE_IN_THE_KERNEL + chip->controller_microcode = + is_1e ? &snd_ymfpci_controller_1e_microcode + : &snd_ymfpci_controller_microcode; +#else + return err; +#endif + } + return 0; +} + static void snd_ymfpci_download_image(struct snd_ymfpci *chip) { int i; u16 ctrl; - unsigned long *inst; + u32 *inst; snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000); snd_ymfpci_disable_dsp(chip); @@ -1992,21 +2065,12 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip) snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); /* setup DSP instruction code */ + inst = (u32 *)chip->dsp_microcode->data; for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) - snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]); /* setup control instruction code */ - switch (chip->device_id) { - case PCI_DEVICE_ID_YAMAHA_724F: - case PCI_DEVICE_ID_YAMAHA_740C: - case PCI_DEVICE_ID_YAMAHA_744: - case PCI_DEVICE_ID_YAMAHA_754: - inst = CntrlInst1E; - break; - default: - inst = CntrlInst; - break; - } + inst = (u32 *)chip->controller_microcode->data; for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]); @@ -2160,6 +2224,15 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); pci_disable_device(chip->pci); +#ifdef FIRMWARE_IN_THE_KERNEL + if (chip->dsp_microcode != &snd_ymfpci_dsp_microcode) +#endif + release_firmware(chip->dsp_microcode); +#ifdef FIRMWARE_IN_THE_KERNEL + if (chip->controller_microcode != &snd_ymfpci_controller_microcode && + chip->controller_microcode != &snd_ymfpci_controller_1e_microcode) +#endif + release_firmware(chip->controller_microcode); kfree(chip); return 0; } @@ -2315,6 +2388,12 @@ int __devinit snd_ymfpci_create(struct snd_card *card, return -EIO; } + err = snd_ymfpci_request_firmware(chip); + if (err < 0) { + snd_printk(KERN_ERR "firmware request failed: %d\n", err); + snd_ymfpci_free(chip); + return err; + } snd_ymfpci_download_image(chip); udelay(100); /* seems we need a delay after downloading image.. */ -- cgit v1.2.3-59-g8ed1b From f11a96d5cd94202479e603f9dfaff6e92f342135 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:26:55 +0200 Subject: [ALSA] ASoC pxa2xx DMA support This patch adds pxa2xx ASoC DMA audio support. It's based on sound/arm/pxa-pcm.c by Nicolas Pitre with the following differences. o Modified driver structure to use ASoC core PCM callbacks and data structures. o Registration with ASoC core. From: Liam Girdwood Signed-off-by: Nicolas Pitre Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-pcm.c | 363 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/pxa/pxa2xx-pcm.h | 48 ++++++ 2 files changed, 411 insertions(+) create mode 100644 sound/soc/pxa/pxa2xx-pcm.c create mode 100644 sound/soc/pxa/pxa2xx-pcm.h diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c new file mode 100644 index 000000000000..ff32f892287e --- /dev/null +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -0,0 +1,363 @@ +/* + * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pxa2xx-pcm.h" + +static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 32, + .period_bytes_max = 8192 - 32, + .periods_min = 1, + .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), + .buffer_bytes_max = 128 * 1024, + .fifo_size = 32, +}; + +struct pxa2xx_runtime_data { + int dma_ch; + struct pxa2xx_pcm_dma_params *params; + pxa_dma_desc *dma_desc_array; + dma_addr_t dma_desc_array_phys; +}; + +static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) +{ + struct snd_pcm_substream *substream = dev_id; + struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + int dcsr; + + dcsr = DCSR(dma_ch); + DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; + + if (dcsr & DCSR_ENDINTR) { + snd_pcm_period_elapsed(substream); + } else { + printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", + prtd->params->name, dma_ch, dcsr ); + } +} + +static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pxa2xx_runtime_data *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct pxa2xx_pcm_dma_params *dma = rtd->cpu_dai->dma_data; + size_t totsize = params_buffer_bytes(params); + size_t period = params_period_bytes(params); + pxa_dma_desc *dma_desc; + dma_addr_t dma_buff_phys, next_desc_phys; + int ret; + + /* this may get called several times by oss emulation + * with different params */ + if (prtd->params == NULL) { + prtd->params = dma; + ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + pxa2xx_pcm_dma_irq, substream); + if (ret < 0) + return ret; + prtd->dma_ch = ret; + } else if (prtd->params != dma) { + pxa_free_dma(prtd->dma_ch); + prtd->params = dma; + ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + pxa2xx_pcm_dma_irq, substream); + if (ret < 0) + return ret; + prtd->dma_ch = ret; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = totsize; + + dma_desc = prtd->dma_desc_array; + next_desc_phys = prtd->dma_desc_array_phys; + dma_buff_phys = runtime->dma_addr; + do { + next_desc_phys += sizeof(pxa_dma_desc); + dma_desc->ddadr = next_desc_phys; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_desc->dsadr = dma_buff_phys; + dma_desc->dtadr = prtd->params->dev_addr; + } else { + dma_desc->dsadr = prtd->params->dev_addr; + dma_desc->dtadr = dma_buff_phys; + } + if (period > totsize) + period = totsize; + dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc++; + dma_buff_phys += period; + } while (totsize -= period); + dma_desc[-1].ddadr = prtd->dma_desc_array_phys; + + return 0; +} + +static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + + if (prtd && prtd->params) + *prtd->params->drcmr = 0; + + if (prtd->dma_ch) { + snd_pcm_set_runtime_buffer(substream, NULL); + pxa_free_dma(prtd->dma_ch); + prtd->dma_ch = 0; + } + + return 0; +} + +static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + + DCSR(prtd->dma_ch) &= ~DCSR_RUN; + DCSR(prtd->dma_ch) = 0; + DCMD(prtd->dma_ch) = 0; + *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + + return 0; +} + +static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; + DCSR(prtd->dma_ch) = DCSR_RUN; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + DCSR(prtd->dma_ch) &= ~DCSR_RUN; + break; + + case SNDRV_PCM_TRIGGER_RESUME: + DCSR(prtd->dma_ch) |= DCSR_RUN; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; + DCSR(prtd->dma_ch) |= DCSR_RUN; + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t +pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pxa2xx_runtime_data *prtd = runtime->private_data; + + dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); + snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); + + if (x == runtime->buffer_size) + x = 0; + return x; +} + +static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pxa2xx_runtime_data *prtd; + int ret; + + snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware); + + /* + * For mysterious reasons (and despite what the manual says) + * playback samples are lost if the DMA count is not a multiple + * of the DMA burst size. Let's add a rule to enforce that. + */ + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret) + goto out; + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret) + goto out; + + prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL); + if (prtd == NULL) { + ret = -ENOMEM; + goto out; + } + + prtd->dma_desc_array = + dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, + &prtd->dma_desc_array_phys, GFP_KERNEL); + if (!prtd->dma_desc_array) { + ret = -ENOMEM; + goto err1; + } + + runtime->private_data = prtd; + return 0; + + err1: + kfree(prtd); + out: + return ret; +} + +static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pxa2xx_runtime_data *prtd = runtime->private_data; + + dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, + prtd->dma_desc_array, prtd->dma_desc_array_phys); + kfree(prtd); + return 0; +} + +static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +struct snd_pcm_ops pxa2xx_pcm_ops = { + .open = pxa2xx_pcm_open, + .close = pxa2xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pxa2xx_pcm_hw_params, + .hw_free = pxa2xx_pcm_hw_free, + .prepare = pxa2xx_pcm_prepare, + .trigger = pxa2xx_pcm_trigger, + .pointer = pxa2xx_pcm_pointer, + .mmap = pxa2xx_pcm_mmap, +}; + +static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + return 0; +} + +static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; + +int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai, + struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &pxa2xx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_32BIT_MASK; + + if (dai->playback.channels_min) { + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +struct snd_soc_platform pxa2xx_soc_platform = { + .name = "pxa2xx-audio", + .pcm_ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, +}; + +EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h new file mode 100644 index 000000000000..0b55f070da27 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-pcm.h @@ -0,0 +1,48 @@ +/* + * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _PXA2XX_PCM_H +#define _PXA2XX_PCM_H + +struct pxa2xx_pcm_dma_params { + char *name; /* stream identifier */ + u32 dcmd; /* DMA descriptor dcmd field */ + volatile u32 *drcmr; /* the DMA request channel to use */ + u32 dev_addr; /* device physical address for DMA */ +}; + +struct pxa2xx_gpio { + u32 sys; + u32 rx; + u32 tx; + u32 clk; + u32 frm; +}; + +/* pxa2xx DAI ID's */ +#define PXA2XX_DAI_AC97_HIFI 0 +#define PXA2XX_DAI_AC97_AUX 1 +#define PXA2XX_DAI_AC97_MIC 2 +#define PXA2XX_DAI_I2S 0 +#define PXA2XX_DAI_SSP1 0 +#define PXA2XX_DAI_SSP2 1 +#define PXA2XX_DAI_SSP3 2 + +extern struct snd_soc_cpu_dai pxa_ac97_dai[3]; +extern struct snd_soc_cpu_dai pxa_i2s_dai; +extern struct snd_soc_cpu_dai pxa_ssp_dai[3]; + +/* platform data */ +extern struct snd_soc_platform pxa2xx_soc_platform; +extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; + +#endif -- cgit v1.2.3-59-g8ed1b From 3e7cc3d3d1c435f83533b8bf2cf1833855be2901 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:28:10 +0200 Subject: [ALSA] ASoC pxa2xx I2S support This patch adds pxa2xx I2S ASoC audio support. Features:- o Supports playback/capture o 16 bit PCM o 8k - 96k sample rates o Supports master and slave mode. From: Liam Girdwood Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-i2s.c | 306 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 sound/soc/pxa/pxa2xx-i2s.c diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c new file mode 100644 index 000000000000..c3b7a4bb7bd7 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -0,0 +1,306 @@ +/* + * pxa2xx-i2s.c -- ALSA Soc Audio Layer + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * 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. + * + * Revision history + * 12th Aug 2005 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pxa2xx-pcm.h" + +/* used to disable sysclk if external crystal is used */ +static int extclk; +module_param(extclk, int, 0); +MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk"); + +struct pxa_i2s_port { + u32 sadiv; + u32 sacr0; + u32 sacr1; + u32 saimr; + int master; +}; +static struct pxa_i2s_port pxa_i2s; + +#define PXA_I2S_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF) + +#define PXA_I2S_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define PXA_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +/* priv is divider */ +static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { + /* pxa2xx I2S frame and clock master modes */ + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_8000, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x48}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_11025, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x34}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_16000, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x24}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_22050, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x1a}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_44100, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0xd}, + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_48000, PXA_I2S_DIR, + SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0xc}, + + /* pxa2xx I2S frame master and clock slave mode */ + {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, SND_SOC_DAITDM_LRDW(0,0), + SNDRV_PCM_FMTBIT_S16_LE, PXA_I2S_RATES, PXA_I2S_DIR, 0, + SND_SOC_FS_ALL, SND_SOC_FSB(64), 0x48}, + +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { + .name = "I2S PCM Stereo out", + .dev_addr = __PREG(SADR), + .drcmr = &DRCMRTXSADR, + .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { + .name = "I2S PCM Stereo in", + .dev_addr = __PREG(SADR), + .drcmr = &DRCMRRXSADR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static struct pxa2xx_gpio gpio_bus[] = { + { /* I2S SoC Slave */ + .rx = GPIO29_SDATA_IN_I2S_MD, + .tx = GPIO30_SDATA_OUT_I2S_MD, + .clk = GPIO28_BITCLK_IN_I2S_MD, + .frm = GPIO31_SYNC_I2S_MD, + }, + { /* I2S SoC Master */ +#ifdef CONFIG_PXA27x + .sys = GPIO113_I2S_SYSCLK_MD, +#else + .sys = GPIO32_SYSCLK_I2S_MD, +#endif + .rx = GPIO29_SDATA_IN_I2S_MD, + .tx = GPIO30_SDATA_OUT_I2S_MD, + .clk = GPIO28_BITCLK_OUT_I2S_MD, + .frm = GPIO31_SYNC_I2S_MD, + }, +}; + +static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + if (!rtd->cpu_dai->active) { + SACR0 |= SACR0_RST; + SACR0 = 0; + } + + return 0; +} + +/* wait for I2S controller to be ready */ +static int pxa_i2s_wait(void) +{ + int i; + + /* flush the Rx FIFO */ + for(i = 0; i < 16; i++) + SADR; + return 0; +} + +static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + pxa_i2s.master = 0; + if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS) + pxa_i2s.master = 1; + + if (pxa_i2s.master && !extclk) + pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); + + pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); + pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); + pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); + pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); + pxa_set_cken(CKEN8_I2S, 1); + pxa_i2s_wait(); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; + else + rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; + + /* is port used by another stream */ + if (!(SACR0 & SACR0_ENB)) { + + SACR0 = 0; + SACR1 = 0; + if (pxa_i2s.master) + SACR0 |= SACR0_BCKD; + + SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); + + if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J) + SACR1 |= SACR1_AMSL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + SAIMR |= SAIMR_TFS; + else + SAIMR |= SAIMR_RFS; + + SADIV = rtd->cpu_dai->dai_runtime.priv; + return 0; +} + +static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + SACR0 |= SACR0_ENB; + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + SACR1 |= SACR1_DRPL; + SAIMR &= ~SAIMR_TFS; + } else { + SACR1 |= SACR1_DREC; + SAIMR &= ~SAIMR_RFS; + } + + if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { + SACR0 &= ~SACR0_ENB; + pxa_i2s_wait(); + pxa_set_cken(CKEN8_I2S, 0); + } +} + +#ifdef CONFIG_PM +static int pxa2xx_i2s_suspend(struct platform_device *dev, + struct snd_soc_cpu_dai *dai) +{ + if (!dai->active) + return 0; + + /* store registers */ + pxa_i2s.sacr0 = SACR0; + pxa_i2s.sacr1 = SACR1; + pxa_i2s.saimr = SAIMR; + pxa_i2s.sadiv = SADIV; + + /* deactivate link */ + SACR0 &= ~SACR0_ENB; + pxa_i2s_wait(); + return 0; +} + +static int pxa2xx_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + if (!dai->active) + return 0; + + pxa_i2s_wait(); + + SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; + SACR1 = pxa_i2s.sacr1; + SAIMR = pxa_i2s.saimr; + SADIV = pxa_i2s.sadiv; + SACR0 |= SACR0_ENB; + + return 0; +} + +#else +#define pxa2xx_i2s_suspend NULL +#define pxa2xx_i2s_resume NULL +#endif + +/* pxa2xx I2S sysclock is always 256 FS */ +static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface, + struct snd_soc_clock_info *info, unsigned int clk) +{ + return info->rate << 8; +} + +struct snd_soc_cpu_dai pxa_i2s_dai = { + .name = "pxa2xx-i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .suspend = pxa2xx_i2s_suspend, + .resume = pxa2xx_i2s_resume, + .config_sysclk = pxa_i2s_config_sysclk, + .playback = { + .channels_min = 2, + .channels_max = 2,}, + .capture = { + .channels_min = 2, + .channels_max = 2,}, + .ops = { + .startup = pxa2xx_i2s_startup, + .shutdown = pxa2xx_i2s_shutdown, + .trigger = pxa2xx_i2s_trigger, + .hw_params = pxa2xx_i2s_hw_params,}, + .caps = { + .num_modes = ARRAY_SIZE(pxa2xx_i2s_modes), + .mode = pxa2xx_i2s_modes,}, +}; + +EXPORT_SYMBOL_GPL(pxa_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 75b41027662e29822746342865fa8abd941d2604 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:29:03 +0200 Subject: [ALSA] ASoC pxa2xx AC97 support This patch adds pxa2xx AC97 ASoC audio support. It's based on sound/arm/pxa-ac97 by Nicolas Pitre with the following differences. o Modified driver structure to use ASoC core PCM callbacks. o Removed AC97 configuration function (all handled in ASoC core) o Added and exported ASoC DAI configuration table. o Added DMA support for AUX DAC and Mic ADC o Separated out AC97 reset into cold and warm reset functions. From: Liam Girdwood Signed-off-by: Nicolas Pitre Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-ac97.c | 437 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 sound/soc/pxa/pxa2xx-ac97.c diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c new file mode 100644 index 000000000000..28b1985edc05 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -0,0 +1,437 @@ +/* + * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. + * + * Author: Nicolas Pitre + * Created: Dec 02, 2004 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pxa2xx-pcm.h" + +static DEFINE_MUTEX(car_mutex); +static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); +static volatile long gsr_bits; + +#define AC97_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define AC97_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +/* may need to expand this */ +static struct snd_soc_dai_mode pxa2xx_ac97_modes[] = { + { + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = AC97_RATES, + .pcmdir = AC97_DIR, + }, +}; + +/* + * Beware PXA27x bugs: + * + * o Slot 12 read from modem space will hang controller. + * o CDONE, SDONE interrupt fails after any slot 12 IO. + * + * We therefore have an hybrid approach for waiting on SDONE (interrupt or + * 1 jiffy timeout if interrupt never comes). + */ + +static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + unsigned short val = -1; + volatile u32 *reg_addr; + + mutex_lock(&car_mutex); + + /* set up primary or secondary codec/modem space */ +#ifdef CONFIG_PXA27x + reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +#else + if (reg == AC97_GPIO_STATUS) + reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + else + reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +#endif + reg_addr += (reg >> 1); + +#ifndef CONFIG_PXA27x + if (reg == AC97_GPIO_STATUS) { + /* read from controller cache */ + val = *reg_addr; + goto out; + } +#endif + + /* start read access across the ac97 link */ + GSR = GSR_CDONE | GSR_SDONE; + gsr_bits = 0; + val = *reg_addr; + + wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); + if (!((GSR | gsr_bits) & GSR_SDONE)) { + printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", + __FUNCTION__, reg, GSR | gsr_bits); + val = -1; + goto out; + } + + /* valid data now */ + GSR = GSR_CDONE | GSR_SDONE; + gsr_bits = 0; + val = *reg_addr; + /* but we've just started another cycle... */ + wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); + +out: mutex_unlock(&car_mutex); + return val; +} + +static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + volatile u32 *reg_addr; + + mutex_lock(&car_mutex); + + /* set up primary or secondary codec/modem space */ +#ifdef CONFIG_PXA27x + reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +#else + if (reg == AC97_GPIO_STATUS) + reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + else + reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +#endif + reg_addr += (reg >> 1); + + GSR = GSR_CDONE | GSR_SDONE; + gsr_bits = 0; + *reg_addr = val; + wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); + if (!((GSR | gsr_bits) & GSR_CDONE)) + printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", + __FUNCTION__, reg, GSR | gsr_bits); + + mutex_unlock(&car_mutex); +} + +static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) +{ + gsr_bits = 0; + +#ifdef CONFIG_PXA27x + /* warm reset broken on Bulverde, + so manually keep AC97 reset high */ + pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); + udelay(10); + GCR |= GCR_WARM_RST; + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + udelay(500); +#else + GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; + wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +#endif + + if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) + printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", + __FUNCTION__, gsr_bits); + + GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); + GCR |= GCR_SDONE_IE|GCR_CDONE_IE; +} + +static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) +{ + GCR &= GCR_COLD_RST; /* clear everything but nCRST */ + GCR &= ~GCR_COLD_RST; /* then assert nCRST */ + + gsr_bits = 0; +#ifdef CONFIG_PXA27x + /* PXA27x Developers Manual section 13.5.2.2.1 */ + pxa_set_cken(1 << 31, 1); + udelay(5); + pxa_set_cken(1 << 31, 0); + GCR = GCR_COLD_RST; + udelay(50); +#else + GCR = GCR_COLD_RST; + GCR |= GCR_CDONE_IE|GCR_SDONE_IE; + wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +#endif + + if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) + printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", + __FUNCTION__, gsr_bits); + + GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); + GCR |= GCR_SDONE_IE|GCR_CDONE_IE; +} + +static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) +{ + long status; + + status = GSR; + if (status) { + GSR = status; + gsr_bits |= status; + wake_up(&gsr_wq); + +#ifdef CONFIG_PXA27x + /* Although we don't use those we still need to clear them + since they tend to spuriously trigger when MMC is used + (hardware bug? go figure)... */ + MISR = MISR_EOC; + PISR = PISR_EOC; + MCSR = MCSR_EOC; +#endif + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = pxa2xx_ac97_read, + .write = pxa2xx_ac97_write, + .warm_reset = pxa2xx_ac97_warm_reset, + .reset = pxa2xx_ac97_cold_reset, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { + .name = "AC97 PCM Stereo out", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRTXPCDR, + .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { + .name = "AC97 PCM Stereo in", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRRXPCDR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { + .name = "AC97 Aux PCM (Slot 5) Mono out", + .dev_addr = __PREG(MODR), + .drcmr = &DRCMRTXMODR, + .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | + DCMD_BURST16 | DCMD_WIDTH2, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { + .name = "AC97 Aux PCM (Slot 5) Mono in", + .dev_addr = __PREG(MODR), + .drcmr = &DRCMRRXMODR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST16 | DCMD_WIDTH2, +}; + +static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { + .name = "AC97 Mic PCM (Slot 6) Mono in", + .dev_addr = __PREG(MCDR), + .drcmr = &DRCMRRXMCDR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST16 | DCMD_WIDTH2, +}; + +#ifdef CONFIG_PM +static int pxa2xx_ac97_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + return 0; +} + +static int pxa2xx_ac97_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); +#ifdef CONFIG_PXA27x + /* Use GPIO 113 as AC97 Reset on Bulverde */ + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +#endif + pxa_set_cken(CKEN2_AC97, 1); + return 0; +} + +#else +#define pxa2xx_ac97_suspend NULL +#define pxa2xx_ac97_resume NULL +#endif + +static int pxa2xx_ac97_probe(struct platform_device *pdev) +{ + int ret; + + ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); + if (ret < 0) + goto err; + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); +#ifdef CONFIG_PXA27x + /* Use GPIO 113 as AC97 Reset on Bulverde */ + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +#endif + pxa_set_cken(CKEN2_AC97, 1); + return 0; + + err: + if (CKEN & CKEN2_AC97) { + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); + } + return ret; +} + +static void pxa2xx_ac97_remove(struct platform_device *pdev) +{ + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); +} + +static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; + else + rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; + + return 0; +} + +static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; + else + rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; + + return 0; +} + +static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + else + rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; + + return 0; +} + +/* + * There is only 1 physical AC97 interface for pxa2xx, but it + * has extra fifo's that can be used for aux DACs and ADCs. + */ +struct snd_soc_cpu_dai pxa_ac97_dai[] = { +{ + .name = "pxa2xx-ac97", + .id = 0, + .type = SND_SOC_DAI_AC97, + .probe = pxa2xx_ac97_probe, + .remove = pxa2xx_ac97_remove, + .suspend = pxa2xx_ac97_suspend, + .resume = pxa2xx_ac97_resume, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2,}, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2,}, + .ops = { + .hw_params = pxa2xx_ac97_hw_params,}, + .caps = { + .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), + .mode = pxa2xx_ac97_modes,}, +}, +{ + .name = "pxa2xx-ac97-aux", + .id = 1, + .type = SND_SOC_DAI_AC97, + .playback = { + .stream_name = "AC97 Aux Playback", + .channels_min = 1, + .channels_max = 1,}, + .capture = { + .stream_name = "AC97 Aux Capture", + .channels_min = 1, + .channels_max = 1,}, + .ops = { + .hw_params = pxa2xx_ac97_hw_aux_params,}, + .caps = { + .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), + .mode = pxa2xx_ac97_modes,}, +}, +{ + .name = "pxa2xx-ac97-mic", + .id = 2, + .type = SND_SOC_DAI_AC97, + .capture = { + .stream_name = "AC97 Mic Capture", + .channels_min = 1, + .channels_max = 1,}, + .ops = { + .hw_params = pxa2xx_ac97_hw_mic_params,}, + .caps = { + .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), + .mode = pxa2xx_ac97_modes,},}, +}; + +EXPORT_SYMBOL_GPL(pxa_ac97_dai); +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From a1eb4b3caf3abd0d1a8474f07d29959e1879bb29 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:31:16 +0200 Subject: [ALSA] ASoC pxa2xx Corgi machine support This patch adds Alsa audio support to the Sharp Zaurus SL-C7x0/C860 (Corgi) machines. From: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/corgi.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 sound/soc/pxa/corgi.c diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c new file mode 100644 index 000000000000..2b1c6e94d1e4 --- /dev/null +++ b/sound/soc/pxa/corgi.c @@ -0,0 +1,361 @@ +/* + * corgi.c -- SoC audio for Corgi + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * Authors: Liam Girdwood + * Richard Purdie + * + * 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. + * + * Revision history + * 30th Nov 2005 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../codecs/wm8731.h" +#include "pxa2xx-pcm.h" + +#define CORGI_HP 0 +#define CORGI_MIC 1 +#define CORGI_LINE 2 +#define CORGI_HEADSET 3 +#define CORGI_HP_OFF 4 +#define CORGI_SPK_ON 0 +#define CORGI_SPK_OFF 1 + + /* audio clock in Hz - rounded from 12.235MHz */ +#define CORGI_AUDIO_CLOCK 12288000 + +static int corgi_jack_func; +static int corgi_spk_func; + +static void corgi_ext_control(struct snd_soc_codec *codec) +{ + int spk = 0, mic = 0, line = 0, hp = 0, hs = 0; + + /* set up jack connection */ + switch (corgi_jack_func) { + case CORGI_HP: + hp = 1; + /* set = unmute headphone */ + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + break; + case CORGI_MIC: + mic = 1; + /* reset = mute headphone */ + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + break; + case CORGI_LINE: + line = 1; + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + break; + case CORGI_HEADSET: + hs = 1; + mic = 1; + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + break; + } + + if (corgi_spk_func == CORGI_SPK_ON) + spk = 1; + + /* set the enpoints to their new connetion states */ + snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic); + snd_soc_dapm_set_endpoint(codec, "Line Jack", line); + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); + + /* signal a DAPM event */ + snd_soc_dapm_sync_endpoints(codec); +} + +static int corgi_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* check the jack status at stream startup */ + corgi_ext_control(codec); + return 0; +} + +/* we need to unmute the HP at shutdown as the mute burns power on corgi */ +static int corgi_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* set = unmute headphone */ + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + return 0; +} + +static struct snd_soc_ops corgi_ops = { + .startup = corgi_startup, + .shutdown = corgi_shutdown, +}; + +static int corgi_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = corgi_jack_func; + return 0; +} + +static int corgi_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (corgi_jack_func == ucontrol->value.integer.value[0]) + return 0; + + corgi_jack_func = ucontrol->value.integer.value[0]; + corgi_ext_control(codec); + return 1; +} + +static int corgi_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = corgi_spk_func; + return 0; +} + +static int corgi_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (corgi_spk_func == ucontrol->value.integer.value[0]) + return 0; + + corgi_spk_func = ucontrol->value.integer.value[0]; + corgi_ext_control(codec); + return 1; +} + +static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); + else + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); + + return 0; +} + +static int corgi_mic_event(struct snd_soc_dapm_widget *w, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); + else + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); + + return 0; +} + +/* corgi machine dapm widgets */ +static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { +SND_SOC_DAPM_HP("Headphone Jack", NULL), +SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event), +SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event), +SND_SOC_DAPM_LINE("Line Jack", NULL), +SND_SOC_DAPM_HP("Headset Jack", NULL), +}; + +/* Corgi machine audio map (connections to the codec pins) */ +static const char *audio_map[][3] = { + + /* headset Jack - in = micin, out = LHPOUT*/ + {"Headset Jack", NULL, "LHPOUT"}, + + /* headphone connected to LHPOUT1, RHPOUT1 */ + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + /* speaker connected to LOUT, ROUT */ + {"Ext Spk", NULL, "ROUT"}, + {"Ext Spk", NULL, "LOUT"}, + + /* mic is connected to MICIN (via right channel of headphone jack) */ + {"MICIN", NULL, "Mic Jack"}, + + /* Same as the above but no mic bias for line signals */ + {"MICIN", NULL, "Line Jack"}, + + {NULL, NULL, NULL}, +}; + +static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", + "Off"}; +static const char *spk_function[] = {"On", "Off"}; +static const struct soc_enum corgi_enum[] = { + SOC_ENUM_SINGLE_EXT(5, jack_function), + SOC_ENUM_SINGLE_EXT(2, spk_function), +}; + +static const struct snd_kcontrol_new wm8731_corgi_controls[] = { + SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack, + corgi_set_jack), + SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk, + corgi_set_spk), +}; + +/* + * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device + */ +static int corgi_wm8731_init(struct snd_soc_codec *codec) +{ + int i, err; + + snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + + /* Add corgi specific controls */ + for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL)); + if (err < 0) + return err; + } + + /* Add corgi specific widgets */ + for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); + } + + /* Set up corgi specific audio path audio_map */ + for(i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +static unsigned int corgi_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) +{ + if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { + /* pxa2xx is i2s master */ + switch (info->rate) { + case 44100: + case 88200: + /* configure codec digital filters for 44.1, 88.2 */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + 11289600); + break; + default: + /* configure codec digital filters for all other rates */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + CORGI_AUDIO_CLOCK); + break; + } + /* config pxa i2s as master */ + return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, + CORGI_AUDIO_CLOCK); + } else { + /* codec is i2s master - + * only configure codec DAI clock and filters */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + CORGI_AUDIO_CLOCK); + } +} + +/* corgi digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link corgi_dai = { + .name = "WM8731", + .stream_name = "WM8731", + .cpu_dai = &pxa_i2s_dai, + .codec_dai = &wm8731_dai, + .init = corgi_wm8731_init, + .config_sysclk = corgi_config_sysclk, +}; + +/* corgi audio machine driver */ +static struct snd_soc_machine snd_soc_machine_corgi = { + .name = "Corgi", + .dai_link = &corgi_dai, + .num_links = 1, + .ops = &corgi_ops, +}; + +/* corgi audio private data */ +static struct wm8731_setup_data corgi_wm8731_setup = { + .i2c_address = 0x1b, +}; + +/* corgi audio subsystem */ +static struct snd_soc_device corgi_snd_devdata = { + .machine = &snd_soc_machine_corgi, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8731, + .codec_data = &corgi_wm8731_setup, +}; + +static struct platform_device *corgi_snd_device; + +static int __init corgi_init(void) +{ + int ret; + + if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky())) + return -ENODEV; + + corgi_snd_device = platform_device_alloc("soc-audio", -1); + if (!corgi_snd_device) + return -ENOMEM; + + platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata); + corgi_snd_devdata.dev = &corgi_snd_device->dev; + ret = platform_device_add(corgi_snd_device); + + if (ret) + platform_device_put(corgi_snd_device); + + return ret; +} + +static void __exit corgi_exit(void) +{ + platform_device_unregister(corgi_snd_device); +} + +module_init(corgi_init); +module_exit(corgi_exit); + +/* Module information */ +MODULE_AUTHOR("Richard Purdie"); +MODULE_DESCRIPTION("ALSA SoC Corgi"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 7fb290d03af69bfca5876573ac0eada40bd4e292 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:32:13 +0200 Subject: [ALSA] ASoC pxa2xx Spitz machine support This patch adds Alsa audio support to the Sharp Zaurus SL-C1000/SL-C3x00 (Akita/Spitz) machines. From: Liam Girdwood Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/spitz.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 sound/soc/pxa/spitz.c diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c new file mode 100644 index 000000000000..17c8e61efe6f --- /dev/null +++ b/sound/soc/pxa/spitz.c @@ -0,0 +1,374 @@ +/* + * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * Authors: Liam Girdwood + * Richard Purdie + * + * 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. + * + * Revision history + * 30th Nov 2005 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/wm8750.h" +#include "pxa2xx-pcm.h" + +#define SPITZ_HP 0 +#define SPITZ_MIC 1 +#define SPITZ_LINE 2 +#define SPITZ_HEADSET 3 +#define SPITZ_HP_OFF 4 +#define SPITZ_SPK_ON 0 +#define SPITZ_SPK_OFF 1 + + /* audio clock in Hz - rounded from 12.235MHz */ +#define SPITZ_AUDIO_CLOCK 12288000 + +static int spitz_jack_func; +static int spitz_spk_func; + +static void spitz_ext_control(struct snd_soc_codec *codec) +{ + if (spitz_spk_func == SPITZ_SPK_ON) + snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); + else + snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0); + + /* set up jack connection */ + switch (spitz_jack_func) { + case SPITZ_HP: + /* enable and unmute hp jack, disable mic bias */ + snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); + set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); + break; + case SPITZ_MIC: + /* enable mic jack and bias, mute hp */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); + break; + case SPITZ_LINE: + /* enable line jack, disable mic bias and mute hp */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Line Jack", 1); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); + break; + case SPITZ_HEADSET: + /* enable and unmute headset jack enable mic bias, mute L hp */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); + snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); + set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); + break; + case SPITZ_HP_OFF: + + /* jack removed, everything off */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); + snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); + break; + } + snd_soc_dapm_sync_endpoints(codec); +} + +static int spitz_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* check the jack status at stream startup */ + spitz_ext_control(codec); + return 0; +} + +static struct snd_soc_ops spitz_ops = { + .startup = spitz_startup, +}; + +static int spitz_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = spitz_jack_func; + return 0; +} + +static int spitz_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (spitz_jack_func == ucontrol->value.integer.value[0]) + return 0; + + spitz_jack_func = ucontrol->value.integer.value[0]; + spitz_ext_control(codec); + return 1; +} + +static int spitz_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = spitz_spk_func; + return 0; +} + +static int spitz_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (spitz_spk_func == ucontrol->value.integer.value[0]) + return 0; + + spitz_spk_func = ucontrol->value.integer.value[0]; + spitz_ext_control(codec); + return 1; +} + +static int spitz_mic_bias(struct snd_soc_dapm_widget *w, int event) +{ + if (machine_is_borzoi() || machine_is_spitz()) { + if (SND_SOC_DAPM_EVENT_ON(event)) + set_scoop_gpio(&spitzscoop2_device.dev, + SPITZ_SCP2_MIC_BIAS); + else + reset_scoop_gpio(&spitzscoop2_device.dev, + SPITZ_SCP2_MIC_BIAS); + } + + if (machine_is_akita()) { + if (SND_SOC_DAPM_EVENT_ON(event)) + akita_set_ioexp(&akitaioexp_device.dev, + AKITA_IOEXP_MIC_BIAS); + else + akita_reset_ioexp(&akitaioexp_device.dev, + AKITA_IOEXP_MIC_BIAS); + } + return 0; +} + +/* spitz machine dapm widgets */ +static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_LINE("Line Jack", NULL), + + /* headset is a mic and mono headphone */ + SND_SOC_DAPM_HP("Headset Jack", NULL), +}; + +/* Spitz machine audio_map */ +static const char *audio_map[][3] = { + + /* headphone connected to LOUT1, ROUT1 */ + {"Headphone Jack", NULL, "LOUT1"}, + {"Headphone Jack", NULL, "ROUT1"}, + + /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ + {"Headset Jack", NULL, "ROUT1"}, + + /* ext speaker connected to LOUT2, ROUT2 */ + {"Ext Spk", NULL , "ROUT2"}, + {"Ext Spk", NULL , "LOUT2"}, + + /* mic is connected to input 1 - with bias */ + {"LINPUT1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Mic Jack"}, + + /* line is connected to input 1 - no bias */ + {"LINPUT1", NULL, "Line Jack"}, + + {NULL, NULL, NULL}, +}; + +static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", + "Off"}; +static const char *spk_function[] = {"On", "Off"}; +static const struct soc_enum spitz_enum[] = { + SOC_ENUM_SINGLE_EXT(5, jack_function), + SOC_ENUM_SINGLE_EXT(2, spk_function), +}; + +static const struct snd_kcontrol_new wm8750_spitz_controls[] = { + SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, + spitz_set_jack), + SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, + spitz_set_spk), +}; + +/* + * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device + */ +static int spitz_wm8750_init(struct snd_soc_codec *codec) +{ + int i, err; + + /* NC codec pins */ + snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0); + snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0); + snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0); + snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0); + snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0); + snd_soc_dapm_set_endpoint(codec, "OUT3", 0); + snd_soc_dapm_set_endpoint(codec, "MONO", 0); + + /* Add spitz specific controls */ + for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); + if (err < 0) + return err; + } + + /* Add spitz specific widgets */ + for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); + } + + /* Set up spitz specific audio path audio_map */ + for (i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +static unsigned int spitz_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) +{ + if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { + /* pxa2xx is i2s master */ + switch (info->rate) { + case 11025: + case 22050: + case 44100: + case 88200: + /* configure codec digital filters + * for 11.025, 22.05, 44.1, 88.2 */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + 11289600); + break; + default: + /* configure codec digital filters for all other rates */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + SPITZ_AUDIO_CLOCK); + break; + } + /* configure pxa2xx i2s interface clocks as master */ + return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, + SPITZ_AUDIO_CLOCK); + } else { + /* codec is i2s master - only configure codec DAI clock */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + SPITZ_AUDIO_CLOCK); + } +} + +/* spitz digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link spitz_dai = { + .name = "wm8750", + .stream_name = "WM8750", + .cpu_dai = &pxa_i2s_dai, + .codec_dai = &wm8750_dai, + .init = spitz_wm8750_init, + .config_sysclk = spitz_config_sysclk, +}; + +/* spitz audio machine driver */ +static struct snd_soc_machine snd_soc_machine_spitz = { + .name = "Spitz", + .dai_link = &spitz_dai, + .num_links = 1, + .ops = &spitz_ops, +}; + +/* spitz audio private data */ +static struct wm8750_setup_data spitz_wm8750_setup = { + .i2c_address = 0x1b, +}; + +/* spitz audio subsystem */ +static struct snd_soc_device spitz_snd_devdata = { + .machine = &snd_soc_machine_spitz, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8750, + .codec_data = &spitz_wm8750_setup, +}; + +static struct platform_device *spitz_snd_device; + +static int __init spitz_init(void) +{ + int ret; + + if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) + return -ENODEV; + + spitz_snd_device = platform_device_alloc("soc-audio", -1); + if (!spitz_snd_device) + return -ENOMEM; + + platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata); + spitz_snd_devdata.dev = &spitz_snd_device->dev; + ret = platform_device_add(spitz_snd_device); + + if (ret) + platform_device_put(spitz_snd_device); + + return ret; +} + +static void __exit spitz_exit(void) +{ + platform_device_unregister(spitz_snd_device); +} + +module_init(spitz_init); +module_exit(spitz_exit); + +MODULE_AUTHOR("Richard Purdie"); +MODULE_DESCRIPTION("ALSA SoC Spitz"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 1b49cb987030c09ca763c1dabd5c5e33f669e530 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:33:09 +0200 Subject: [ALSA] ASoC pxa2xx Tosa machine support This patch adds Alsa audio support to the Sharp Zaurus SL-C6000 (Tosa) machine. From: Liam Girdwood Signed-off-by: Dirk Opfer Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/tosa.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 sound/soc/pxa/tosa.c diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c new file mode 100644 index 000000000000..8c3c6b0534d6 --- /dev/null +++ b/sound/soc/pxa/tosa.c @@ -0,0 +1,287 @@ +/* + * tosa.c -- SoC audio for Tosa + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * Authors: Liam Girdwood + * Richard Purdie + * + * 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. + * + * Revision history + * 30th Nov 2005 Initial version. + * + * GPIO's + * 1 - Jack Insertion + * 5 - Hookswitch (headset answer/hang up switch) + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../codecs/wm9712.h" +#include "pxa2xx-pcm.h" + +static struct snd_soc_machine tosa; + +#define TOSA_HP 0 +#define TOSA_MIC_INT 1 +#define TOSA_HEADSET 2 +#define TOSA_HP_OFF 3 +#define TOSA_SPK_ON 0 +#define TOSA_SPK_OFF 1 + +static int tosa_jack_func; +static int tosa_spk_func; + +static void tosa_ext_control(struct snd_soc_codec *codec) +{ + int spk = 0, mic_int = 0, hp = 0, hs = 0; + + /* set up jack connection */ + switch (tosa_jack_func) { + case TOSA_HP: + hp = 1; + break; + case TOSA_MIC_INT: + mic_int = 1; + break; + case TOSA_HEADSET: + hs = 1; + break; + } + + if (tosa_spk_func == TOSA_SPK_ON) + spk = 1; + + snd_soc_dapm_set_endpoint(codec, "Speaker", spk); + snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int); + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); + snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); + snd_soc_dapm_sync_endpoints(codec); +} + +static int tosa_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* check the jack status at stream startup */ + tosa_ext_control(codec); + return 0; +} + +static struct snd_soc_ops tosa_ops = { + .startup = tosa_startup, +}; + +static int tosa_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tosa_jack_func; + return 0; +} + +static int tosa_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (tosa_jack_func == ucontrol->value.integer.value[0]) + return 0; + + tosa_jack_func = ucontrol->value.integer.value[0]; + tosa_ext_control(codec); + return 1; +} + +static int tosa_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tosa_spk_func; + return 0; +} + +static int tosa_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (tosa_spk_func == ucontrol->value.integer.value[0]) + return 0; + + tosa_spk_func = ucontrol->value.integer.value[0]; + tosa_ext_control(codec); + return 1; +} + +/* tosa dapm event handlers */ +static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); + else + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); + return 0; +} + +/* tosa machine dapm widgets */ +static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = { +SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event), +SND_SOC_DAPM_HP("Headset Jack", NULL), +SND_SOC_DAPM_MIC("Mic (Internal)", NULL), +SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +/* tosa audio map */ +static const char *audio_map[][3] = { + + /* headphone connected to HPOUTL, HPOUTR */ + {"Headphone Jack", NULL, "HPOUTL"}, + {"Headphone Jack", NULL, "HPOUTR"}, + + /* ext speaker connected to LOUT2, ROUT2 */ + {"Speaker", NULL, "LOUT2"}, + {"Speaker", NULL, "ROUT2"}, + + /* internal mic is connected to mic1, mic2 differential - with bias */ + {"MIC1", NULL, "Mic Bias"}, + {"MIC2", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Mic (Internal)"}, + + /* headset is connected to HPOUTR, and LINEINR with bias */ + {"Headset Jack", NULL, "HPOUTR"}, + {"LINEINR", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Jack"}, + + {NULL, NULL, NULL}, +}; + +static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", + "Off"}; +static const char *spk_function[] = {"On", "Off"}; +static const struct soc_enum tosa_enum[] = { + SOC_ENUM_SINGLE_EXT(5, jack_function), + SOC_ENUM_SINGLE_EXT(2, spk_function), +}; + +static const struct snd_kcontrol_new tosa_controls[] = { + SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack, + tosa_set_jack), + SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk, + tosa_set_spk), +}; + +static int tosa_ac97_init(struct snd_soc_codec *codec) +{ + int i, err; + + snd_soc_dapm_set_endpoint(codec, "OUT3", 0); + snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0); + + /* add tosa specific controls */ + for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&tosa_controls[i],codec, NULL)); + if (err < 0) + return err; + } + + /* add tosa specific widgets */ + for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]); + } + + /* set up tosa specific audio path audio_map */ + for (i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +static struct snd_soc_dai_link tosa_dai[] = { +{ + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], + .init = tosa_ac97_init, +}, +{ + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], +}, +}; + +static struct snd_soc_machine tosa = { + .name = "Tosa", + .dai_link = tosa_dai, + .num_links = ARRAY_SIZE(tosa_dai), + .ops = &tosa_ops, +}; + +static struct snd_soc_device tosa_snd_devdata = { + .machine = &tosa, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm9712, +}; + +static struct platform_device *tosa_snd_device; + +static int __init tosa_init(void) +{ + int ret; + + if (!machine_is_tosa()) + return -ENODEV; + + tosa_snd_device = platform_device_alloc("soc-audio", -1); + if (!tosa_snd_device) + return -ENOMEM; + + platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata); + tosa_snd_devdata.dev = &tosa_snd_device->dev; + ret = platform_device_add(tosa_snd_device); + + if (ret) + platform_device_put(tosa_snd_device); + + return ret; +} + +static void __exit tosa_exit(void) +{ + platform_device_unregister(tosa_snd_device); +} + +module_init(tosa_init); +module_exit(tosa_exit); + +/* Module information */ +MODULE_AUTHOR("Richard Purdie"); +MODULE_DESCRIPTION("ALSA SoC Tosa"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 6e24dd9310b66d6f500a81ee320a8babec529573 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:33:45 +0200 Subject: [ALSA] ASoC pxa2xx Poodle machine support This patch adds Alsa audio support to the Sharp Zaurus SL-C5600 (Poodle) machine. From: Liam Girdwood Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/poodle.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 sound/soc/pxa/poodle.c diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c new file mode 100644 index 000000000000..ee9336086820 --- /dev/null +++ b/sound/soc/pxa/poodle.c @@ -0,0 +1,329 @@ +/* + * poodle.c -- SoC audio for Poodle + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * Authors: Liam Girdwood + * Richard Purdie + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../codecs/wm8731.h" +#include "pxa2xx-pcm.h" + +#define POODLE_HP 1 +#define POODLE_HP_OFF 0 +#define POODLE_SPK_ON 1 +#define POODLE_SPK_OFF 0 + + /* audio clock in Hz - rounded from 12.235MHz */ +#define POODLE_AUDIO_CLOCK 12288000 + +static int poodle_jack_func; +static int poodle_spk_func; + +static void poodle_ext_control(struct snd_soc_codec *codec) +{ + int spk = 0; + + /* set up jack connection */ + if (poodle_jack_func == POODLE_HP) { + /* set = unmute headphone */ + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_L, 1); + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_R, 1); + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + } else { + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_L, 0); + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_R, 0); + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + } + + if (poodle_spk_func == POODLE_SPK_ON) + spk = 1; + + /* set the enpoints to their new connetion states */ + snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); + + /* signal a DAPM event */ + snd_soc_dapm_sync_endpoints(codec); +} + +static int poodle_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* check the jack status at stream startup */ + poodle_ext_control(codec); + return 0; +} + +/* we need to unmute the HP at shutdown as the mute burns power on poodle */ +static int poodle_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + /* set = unmute headphone */ + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_L, 1); + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_R, 1); + return 0; +} + +static struct snd_soc_ops poodle_ops = { + .startup = poodle_startup, + .shutdown = poodle_shutdown, +}; + +static int poodle_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = poodle_jack_func; + return 0; +} + +static int poodle_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (poodle_jack_func == ucontrol->value.integer.value[0]) + return 0; + + poodle_jack_func = ucontrol->value.integer.value[0]; + poodle_ext_control(codec); + return 1; +} + +static int poodle_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = poodle_spk_func; + return 0; +} + +static int poodle_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (poodle_spk_func == ucontrol->value.integer.value[0]) + return 0; + + poodle_spk_func = ucontrol->value.integer.value[0]; + poodle_ext_control(codec); + return 1; +} + +static int poodle_amp_event(struct snd_soc_dapm_widget *w, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_AMP_ON, 0); + else + locomo_gpio_write(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_AMP_ON, 1); + + return 0; +} + +/* poodle machine dapm widgets */ +static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { +SND_SOC_DAPM_HP("Headphone Jack", NULL), +SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), +}; + +/* Corgi machine audio_mapnections to the codec pins */ +static const char *audio_map[][3] = { + + /* headphone connected to LHPOUT1, RHPOUT1 */ + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + /* speaker connected to LOUT, ROUT */ + {"Ext Spk", NULL, "ROUT"}, + {"Ext Spk", NULL, "LOUT"}, + + {NULL, NULL, NULL}, +}; + +static const char *jack_function[] = {"Off", "Headphone"}; +static const char *spk_function[] = {"Off", "On"}; +static const struct soc_enum poodle_enum[] = { + SOC_ENUM_SINGLE_EXT(2, jack_function), + SOC_ENUM_SINGLE_EXT(2, spk_function), +}; + +static const snd_kcontrol_new_t wm8731_poodle_controls[] = { + SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack, + poodle_set_jack), + SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk, + poodle_set_spk), +}; + +/* + * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device + */ +static int poodle_wm8731_init(struct snd_soc_codec *codec) +{ + int i, err; + + snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "MICIN", 1); + + /* Add poodle specific controls */ + for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL)); + if (err < 0) + return err; + } + + /* Add poodle specific widgets */ + for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); + } + + /* Set up poodle specific audio path audio_map */ + for (i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +static unsigned int poodle_config_sysclk(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_clock_info *info) +{ + if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { + /* pxa2xx is i2s master */ + switch (info->rate) { + case 44100: + case 88200: + /* configure codec digital filters for 44.1, 88.2 */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + 11289600); + break; + default: + /* configure codec digital filters for all other rates */ + rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + POODLE_AUDIO_CLOCK); + break; + } + return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, + POODLE_AUDIO_CLOCK); + } else { + /* codec is i2s master - + * only configure codec DAI clock and filters */ + return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, + POODLE_AUDIO_CLOCK); + } +} + +/* poodle digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link poodle_dai = { + .name = "WM8731", + .stream_name = "WM8731", + .cpu_dai = &pxa_i2s_dai, + .codec_dai = &wm8731_dai, + .init = poodle_wm8731_init, + .config_sysclk = poodle_config_sysclk, +}; + +/* poodle audio machine driver */ +static struct snd_soc_machine snd_soc_machine_poodle = { + .name = "Poodle", + .dai_link = &poodle_dai, + .num_links = 1, + .ops = &poodle_ops, +}; + +/* poodle audio private data */ +static struct wm8731_setup_data poodle_wm8731_setup = { + .i2c_address = 0x1b, +}; + +/* poodle audio subsystem */ +static struct snd_soc_device poodle_snd_devdata = { + .machine = &snd_soc_machine_poodle, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8731, + .codec_data = &poodle_wm8731_setup, +}; + +static struct platform_device *poodle_snd_device; + +static int __init poodle_init(void) +{ + int ret; + + if (!machine_is_poodle()) + return -ENODEV; + + locomo_gpio_set_dir(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_AMP_ON, 0); + /* should we mute HP at startup - burning power ?*/ + locomo_gpio_set_dir(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_L, 0); + locomo_gpio_set_dir(&poodle_locomo_device.dev, + POODLE_LOCOMO_GPIO_MUTE_R, 0); + + poodle_snd_device = platform_device_alloc("soc-audio", -1); + if (!poodle_snd_device) + return -ENOMEM; + + platform_set_drvdata(poodle_snd_device, &poodle_snd_devdata); + poodle_snd_devdata.dev = &poodle_snd_device->dev; + ret = platform_device_add(poodle_snd_device); + + if (ret) + platform_device_put(poodle_snd_device); + + return ret; +} + +static void __exit poodle_exit(void) +{ + platform_device_unregister(poodle_snd_device); +} + +module_init(poodle_init); +module_exit(poodle_exit); + +/* Module information */ +MODULE_AUTHOR("Richard Purdie"); +MODULE_DESCRIPTION("ALSA SoC Poodle"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 734c2d4bb7cfccaab79923331efc7422e4e76a8a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Oct 2006 14:34:32 +0200 Subject: [ALSA] ASoC pxa2xx build support This patch builds ASoC pxa2xx support for Corgi, Spitz, Tosa and Poodle Zaurus machines. From: Liam Girdwood Signed-off-by: Richard Purdie Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 2 +- sound/soc/pxa/Kconfig | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/pxa/Makefile | 20 +++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 sound/soc/pxa/Kconfig create mode 100644 sound/soc/pxa/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e7dab5bdc6b2..ec821a57f843 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -23,6 +23,7 @@ config SND_SOC menu "SoC Platforms" depends on SND_SOC source "sound/soc/at91/Kconfig" +source "sound/soc/pxa/Kconfig" endmenu # Supported codecs diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 3e12a1654c49..98e6f49dafc2 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ at91/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig new file mode 100644 index 000000000000..a07598cdab3c --- /dev/null +++ b/sound/soc/pxa/Kconfig @@ -0,0 +1,60 @@ +menu "SoC Audio for the Intel PXA2xx" + +config SND_PXA2XX_SOC + tristate "SoC Audio for the Intel PXA2xx chip" + depends on ARCH_PXA && SND + select SND_PCM + help + Say Y or M if you want to add support for codecs attached to + the PXA2xx AC97, I2S or SSP interface. You will also need + to select the audio interfaces to support below. + +config SND_PXA2XX_AC97 + tristate + select SND_AC97_CODEC + +config SND_PXA2XX_SOC_AC97 + tristate + select SND_AC97_BUS + select SND_SOC_AC97_BUS + +config SND_PXA2XX_SOC_I2S + tristate + +config SND_PXA2XX_SOC_CORGI + tristate "SoC Audio support for Sharp Zaurus SL-C7x0" + depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx + select SND_PXA2XX_SOC_I2S + select SND_SOC_WM8731 + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-C7x0 models (Corgi, Shepherd, Husky). + +config SND_PXA2XX_SOC_SPITZ + tristate "SoC Audio support for Sharp Zaurus SL-Cxx00" + depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 + select SND_PXA2XX_SOC_I2S + select SND_SOC_WM8750 + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita). + +config SND_PXA2XX_SOC_POODLE + tristate "SoC Audio support for Poodle" + depends on SND_PXA2XX_SOC && MACH_POODLE + select SND_PXA2XX_SOC_I2S + select SND_SOC_WM8731 + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-5600 model (Poodle). + +config SND_PXA2XX_SOC_TOSA + tristate "SoC AC97 Audio support for Tosa" + depends on SND_PXA2XX_SOC && MACH_TOSA + select SND_PXA2XX_SOC_AC97 + select SND_SOC_WM9712 + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-C6000x models (Tosa). + +endmenu diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile new file mode 100644 index 000000000000..78e0d6b07d1d --- /dev/null +++ b/sound/soc/pxa/Makefile @@ -0,0 +1,20 @@ +# PXA Platform Support +snd-soc-pxa2xx-objs := pxa2xx-pcm.o +snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o +snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o + +obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o +obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o +obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o + +# PXA Machine Support +snd-soc-corgi-objs := corgi.o +snd-soc-poodle-objs := poodle.o +snd-soc-tosa-objs := tosa.o +snd-soc-spitz-objs := spitz.o + +obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o +obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o +obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o +obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o + -- cgit v1.2.3-59-g8ed1b From c07584c83287ae5a13cc836f69a1d824ad068c66 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Fri, 13 Oct 2006 12:32:16 +0200 Subject: [ALSA] hda-codec - Add support for Medion laptops This patch adds audio support for Medion's line of laptops, based on code shipped with the laptops. Microphone support is still being explored. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 54 ++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 9fef210ab50a..08f63ee53742 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -817,6 +817,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack-6ch-dig 3-jack 6-channel with SPDIF I/O 6stack-dig-demo 6-jack digital for Intel demo board acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc) + medion Medion Laptops auto auto-config reading BIOS (default) ALC861/660 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4e0c3c1b908b..0b14bd17181a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -112,6 +112,7 @@ enum { ALC883_6ST_DIG, ALC888_DEMO_BOARD, ALC883_ACER, + ALC883_MEDION, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -4309,7 +4310,7 @@ static struct hda_verb alc882_init_verbs[] = { static struct hda_verb alc882_eapd_verbs[] = { /* change to EAPD mode */ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, { } }; @@ -4875,6 +4876,41 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { { } /* end */ }; +static snd_kcontrol_new_t alc883_fivestack_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -5082,6 +5118,8 @@ static struct hda_board_config alc883_cfg_tbl[] = { .config = ALC883_ACER }, { .pci_subvendor = 0x1025, .pci_subdevice = 0x009f, .config = ALC883_ACER }, + { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, + .modelname = "medion", .config = ALC883_MEDION } { .modelname = "auto", .config = ALC883_AUTO }, {} }; @@ -5169,6 +5207,20 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, }, + [ALC883_MEDION] = { + .mixers = { alc883_fivestack_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + } + }; -- cgit v1.2.3-59-g8ed1b From 527541f9a8a83eedb4d732657dbfdcd2c4ca8bb4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Oct 2006 12:33:56 +0200 Subject: [ALSA] ASoC DAI capabilities labelling This patch suggested by Takashi changes the DAI capabilities definitions in pxa-i2s.c, at91rm9200-i2s.c, wm8731.c, wm8750.c and wm9712.c to use a label = value style. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 52 +++++-- sound/soc/codecs/wm8731.c | 184 ++++++++++++++++------- sound/soc/codecs/wm8750.c | 316 +++++++++++++++++++++++++++++----------- sound/soc/codecs/wm9712.c | 7 +- sound/soc/pxa/pxa2xx-i2s.c | 91 +++++++++--- 5 files changed, 483 insertions(+), 167 deletions(-) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c index 044a774b4301..91f1daa44f19 100644 --- a/sound/soc/at91/at91rm9200-i2s.c +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -51,24 +51,52 @@ static struct snd_soc_dai_mode at91rm9200_i2s[] = { /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ - { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_8000, AT91RM9200_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 1500, SND_SOC_FSBD(10), (25 << 16 | 74) }, + { + .fmt = AT91RM9200_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = AT91RM9200_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, + .bfs = SND_SOC_FSBD(10), + .priv = (25 << 16 | 74), + }, /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_16000, AT91RM9200_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 750, SND_SOC_FSBD(3) , (7 << 16 | 133) }, + { + .fmt = AT91RM9200_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = AT91RM9200_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 750, + .bfs = SND_SOC_FSBD(3), + .flags (7 << 16 | 133), + }, /* 24k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ - { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_22050, AT91RM9200_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 500, SND_SOC_FSBD(10), (25 << 16 | 24) }, + { + .fmt = AT91RM9200_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = AT91RM9200_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 500, + .bfs = SND_SOC_FSBD(10), + .priv = (25 << 16 | 24), + }, /* 48kHz: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ - { AT91RM9200_I2S_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_48000, AT91RM9200_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 250, SND_SOC_FSBD(5), (13 << 16 | 23) }, + { + .fmt = AT91RM9200_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = AT91RM9200_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, + .bfs SND_SOC_FSBD(5), + .priv = (13 << 16 | 23), + }, }; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index cd0ece650f31..03a6bb9b8773 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -85,74 +85,160 @@ static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { static struct snd_soc_dai_mode wm8731_modes[] = { /* codec frame and clock master modes */ /* 8k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, - 1536, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, - 2304, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, - 1408, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, 0, - 2112, SND_SOC_FSB(64)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8731_DIR, + .fs = 1536, + .bfs = SND_SOC_FSB(64), + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8731_DIR, + .fs = 2304, + .bfs = SND_SOC_FSB(64), + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8731_DIR, + .fs = 1408, + .bfs = SND_SOC_FSB(64), + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8731_DIR, + .fs = 2112, + .bfs = SND_SOC_FSB(64), + }, /* 32k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_32000, WM8731_DIR, 0, - 384, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_32000, WM8731_DIR, 0, - 576, SND_SOC_FSB(64)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = WM8731_DIR, + .fs = 384, + .bfs = SND_SOC_FSB(64), + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = WM8731_DIR, + .fs = 576, + .bfs = SND_SOC_FSB(64), + }, /* 44.1k & 48k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - WM8731_DIR, 0, 256, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - WM8731_DIR, 0, 384, SND_SOC_FSB(64)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8731_DIR, + .fs = 256, + .bfs = SND_SOC_FSB(64), + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8731_DIR, + .fs = 384, + .bfs = SND_SOC_FSB(64), + }, /* 88.2 & 96k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - WM8731_DIR, 0, 128, SND_SOC_FSB(64)}, - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - WM8731_DIR, 0, 192, SND_SOC_FSB(64)}, - + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8731_DIR, + .fs = 128, + .bfs = SND_SOC_FSB(64), + + }, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8731_DIR, + .fs = 192, + .bfs = SND_SOC_FSB(64), + }, /* USB codec frame and clock master modes */ /* 8k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_8000, WM8731_DIR, - SND_SOC_DAI_BFS_DIV, 1500, SND_SOC_FSBD(1)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, + .bfs = SND_SOC_FSBD(1), + }, /* 44.1k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_44100, WM8731_DIR, - SND_SOC_DAI_BFS_DIV, 272, SND_SOC_FSBD(1)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 272, + .bfs = SND_SOC_FSBD(1), + }, /* 48k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_48000, WM8731_DIR, - SND_SOC_DAI_BFS_DIV, 250, SND_SOC_FSBD(1)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, + .bfs = SND_SOC_FSBD(1), + }, /* 88.2k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_88200, WM8731_DIR, - SND_SOC_DAI_BFS_DIV, 136, SND_SOC_FSBD(1)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 136, + .bfs = SND_SOC_FSBD(1), + }, /* 96k */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, SNDRV_PCM_RATE_96000, WM8731_DIR, - SND_SOC_DAI_BFS_DIV, 125, SND_SOC_FSBD(1)}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_96000, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 125, + .bfs = SND_SOC_FSBD(1), + }, /* codec frame and clock slave modes */ - {WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - WM8731_HIFI_BITS, WM8731_RATES, WM8731_DIR, SND_SOC_DAI_BFS_DIV, - SND_SOC_FS_ALL, SND_SOC_FSBD_ALL}, + { + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = WM8731_RATES, + .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSBD_ALL, + }, }; /* diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 6a8b2799b3b1..b07a6ed6aa66 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -97,102 +97,254 @@ static const u16 wm8750_reg[] = { static struct snd_soc_dai_mode wm8750_modes[] = { /* common codec frame and clock master modes */ /* 8k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1536, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1408, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 2304, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 2112, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_8000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1500, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1536, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1408, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 2304, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 2112, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, + .bfs = WM8750_HIFI_FSB, + }, /* 11.025k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1024, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1536, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_11025, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1088, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_11025, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1024, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_11025, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1536, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_11025, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1088, + .bfs = WM8750_HIFI_FSB, + }, /* 16k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 768, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 1152, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 750, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 768, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1152, + .bfs = WM8750_HIFI_FSB + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 750, + .bfs = WM8750_HIFI_FSB, + }, /* 22.05k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 512, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 768, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_22050, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 544, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 512, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 768, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 544, + .bfs = WM8750_HIFI_FSB, + }, /* 32k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 384, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 576, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_16000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 375, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 384, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 576, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 375, + .bfs = WM8750_HIFI_FSB, + }, /* 44.1k & 48k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 256, - WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 384, - WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_44100, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 272, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_48000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 250, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 384, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 272, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, + .bfs = WM8750_HIFI_FSB, + }, /* 88.2k & 96k */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 128, - WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000, WM8750_DIR, SND_SOC_DAI_BFS_DIV, 192, - WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_88200, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 136, WM8750_HIFI_FSB}, - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAITDM_LRDW(0,0), WM8750_HIFI_BITS, SNDRV_PCM_RATE_96000, - WM8750_DIR, SND_SOC_DAI_BFS_DIV, 125, WM8750_HIFI_FSB}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 128, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 192, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 136, + .bfs = WM8750_HIFI_FSB, + }, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_96000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 125, + .bfs = WM8750_HIFI_FSB, + }, /* codec frame and clock slave modes */ - {WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - WM8750_HIFI_BITS, WM8750_HIFI_RATES, WM8750_DIR, - SND_SOC_DAI_BFS_DIV, SND_SOC_FS_ALL, SND_SOC_FSBD_ALL}, + { + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = WM8750_HIFI_RATES, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSBD_ALL, + }, }; /* diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 4850550e2e31..c6b7de426460 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -44,8 +44,11 @@ static int ac97_write(struct snd_soc_codec *codec, /* may need to expand this */ static struct snd_soc_dai_mode ac97_modes[] = { - {0, 0, SNDRV_PCM_FMTBIT_S16_LE, AC97_RATES}, - {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, AC97_RATES}, + { + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE, + .pcmrate = AC97_RATES, + .pcmdir = AC97_DIR, + }, }; /* diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index c3b7a4bb7bd7..db2310f87fbe 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -58,30 +58,77 @@ static struct pxa_i2s_port pxa_i2s; /* priv is divider */ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { /* pxa2xx I2S frame and clock master modes */ - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_8000, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x48}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_11025, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x34}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_16000, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x24}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_22050, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0x1a}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_44100, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0xd}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_RATE_48000, PXA_I2S_DIR, - SND_SOC_DAI_BFS_DIV, 256, SND_SOC_FSBD(4), 0xc}, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x48, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_11025, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x34, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x24, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x1a, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_44100, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0xd, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0xc, + }, /* pxa2xx I2S frame master and clock slave mode */ - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FMTBIT_S16_LE, PXA_I2S_RATES, PXA_I2S_DIR, 0, - SND_SOC_FS_ALL, SND_SOC_FSB(64), 0x48}, - + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = PXA_I2S_RATES, + .pcmdir = PXA_I2S_DIR, + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSB(64), + .priv = 0x48, + }, }; static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { -- cgit v1.2.3-59-g8ed1b From 35f60839b6158f72d2be0dd2764ad772e1d44e8a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Oct 2006 12:46:10 +0200 Subject: [ALSA] hda-codec - Add missing comma Added a missing comma in the medion patch. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0b14bd17181a..1420db43423a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5119,7 +5119,7 @@ static struct hda_board_config alc883_cfg_tbl[] = { { .pci_subvendor = 0x1025, .pci_subdevice = 0x009f, .config = ALC883_ACER }, { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, - .modelname = "medion", .config = ALC883_MEDION } + .modelname = "medion", .config = ALC883_MEDION }, { .modelname = "auto", .config = ALC883_AUTO }, {} }; -- cgit v1.2.3-59-g8ed1b From b5c5fd24b9d34e4670cb339e546bfae7ad316354 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Oct 2006 19:13:41 +0200 Subject: [ALSA] ASoC debug output build breakage This patch fixes a build failure when ASoC debug is enabled. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index eba0a1012531..8d6ff047d7a0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -575,10 +575,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name); - dbg("asoc: rate mask 0x%x \nasoc: min ch %d max ch %d\n" - "asoc: min rate %d max rate %d\n", - runtime->hw.rates, runtime->hw.channels_min, - runtime->hw.channels_max, runtime->hw.rate_min, runtime->hw.rate_max); + dbg("asoc: rate mask 0x%x\n", runtime->hw.rates); + dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, + runtime->hw.channels_max); + dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, + runtime->hw.rate_max); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -- cgit v1.2.3-59-g8ed1b From b3b9c1cbb35125f7e43a323ebe89e7a74e3c1ac2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Oct 2006 20:09:59 +0200 Subject: [ALSA] Remove trailing whitespaces from soc/* files Remove trailing whitespaces from soc/* files added by the conversion to C99-style initialization. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 34 +++---- sound/soc/codecs/wm8731.c | 94 +++++++++--------- sound/soc/codecs/wm8750.c | 210 ++++++++++++++++++++-------------------- sound/soc/pxa/pxa2xx-i2s.c | 58 +++++------ 4 files changed, 198 insertions(+), 198 deletions(-) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c index 91f1daa44f19..8c4d3b999053 100644 --- a/sound/soc/at91/at91rm9200-i2s.c +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -52,9 +52,9 @@ static struct snd_soc_dai_mode at91rm9200_i2s[] = { /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ { - .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, + .fmt = AT91RM9200_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = AT91RM9200_I2S_DIR, .flags = SND_SOC_DAI_BFS_DIV, .fs = 1500, @@ -63,38 +63,38 @@ static struct snd_soc_dai_mode at91rm9200_i2s[] = { }, /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { + { .fmt = AT91RM9200_I2S_DAIFMT, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, .pcmrate = SNDRV_PCM_RATE_16000, .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 750, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 750, .bfs = SND_SOC_FSBD(3), .flags (7 << 16 | 133), }, /* 24k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ - { + { .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_22050, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_22050, .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 500, - .bfs = SND_SOC_FSBD(10), + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 500, + .bfs = SND_SOC_FSBD(10), .priv = (25 << 16 | 24), }, /* 48kHz: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ - { + { .fmt = AT91RM9200_I2S_DAIFMT, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, + .pcmrate = SNDRV_PCM_RATE_48000, .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, - .bfs SND_SOC_FSBD(5), + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, + .bfs SND_SOC_FSBD(5), .priv = (13 << 16 | 23), }, }; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 03a6bb9b8773..9adbd2d401c4 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -87,89 +87,89 @@ static struct snd_soc_dai_mode wm8731_modes[] = { /* 8k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, - .fs = 1536, + .fs = 1536, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, - .fs = 2304, + .fs = 2304, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, - .fs = 1408, + .fs = 1408, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, - .fs = 2112, + .fs = 2112, .bfs = SND_SOC_FSB(64), }, /* 32k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, + .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, .pcmdir = WM8731_DIR, - .fs = 384, + .fs = 384, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_32000, .pcmdir = WM8731_DIR, - .fs = 576, + .fs = 576, .bfs = SND_SOC_FSB(64), }, /* 44.1k & 48k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, + .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmdir = WM8731_DIR, - .fs = 256, + .fs = 256, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, + .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmdir = WM8731_DIR, - .fs = 384, + .fs = 384, .bfs = SND_SOC_FSB(64), }, /* 88.2 & 96k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, + .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmdir = WM8731_DIR, - .fs = 128, + .fs = 128, .bfs = SND_SOC_FSB(64), }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, + .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmdir = WM8731_DIR, - .fs = 192, + .fs = 192, .bfs = SND_SOC_FSB(64), }, @@ -177,66 +177,66 @@ static struct snd_soc_dai_mode wm8731_modes[] = { /* 8k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, .bfs = SND_SOC_FSBD(1), }, /* 44.1k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100, .pcmdir = WM8731_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = 272, + .fs = 272, .bfs = SND_SOC_FSBD(1), }, /* 48k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_48000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_48000, .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, .bfs = SND_SOC_FSBD(1), }, /* 88.2k */ { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200, + .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200, .pcmdir = WM8731_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = 136, + .fs = 136, .bfs = SND_SOC_FSBD(1), }, /* 96k */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_96000, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_96000, .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 125, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 125, .bfs = SND_SOC_FSBD(1), }, /* codec frame and clock slave modes */ { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = WM8731_RATES, + .pcmfmt = WM8731_HIFI_BITS, + .pcmrate = WM8731_RATES, .pcmdir = WM8731_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = SND_SOC_FS_ALL, + .fs = SND_SOC_FS_ALL, .bfs = SND_SOC_FSBD_ALL, }, }; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index b07a6ed6aa66..243da712d9c1 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -99,250 +99,250 @@ static struct snd_soc_dai_mode wm8750_modes[] = { /* 8k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1536, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1536, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1408, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1408, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 2304, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 2304, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 2112, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 2112, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, .bfs = WM8750_HIFI_FSB, }, /* 11.025k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1024, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1024, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1536, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1536, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1088, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1088, .bfs = WM8750_HIFI_FSB, }, /* 16k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 768, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 768, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1152, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1152, .bfs = WM8750_HIFI_FSB }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 750, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 750, .bfs = WM8750_HIFI_FSB, }, /* 22.05k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 512, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 512, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 768, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 768, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 544, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 544, .bfs = WM8750_HIFI_FSB, }, /* 32k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 384, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 384, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 576, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 576, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 375, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 375, .bfs = WM8750_HIFI_FSB, }, /* 44.1k & 48k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, .fs = 256, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, .fs = 384, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 272, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 272, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, .bfs = WM8750_HIFI_FSB, }, /* 88.2k & 96k */ { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, .fs = 128, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, .fs = 192, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_88200, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 136, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 136, .bfs = WM8750_HIFI_FSB, }, { .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, + .pcmfmt = WM8750_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 125, + .pcmdir = WM8750_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 125, .bfs = WM8750_HIFI_FSB, }, /* codec frame and clock slave modes */ { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = WM8750_HIFI_RATES, + .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = WM8750_HIFI_BITS, + .pcmrate = WM8750_HIFI_RATES, .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = SND_SOC_FS_ALL, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = SND_SOC_FS_ALL, .bfs = SND_SOC_FSBD_ALL, }, }; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index db2310f87fbe..99f1da32744b 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -60,61 +60,61 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { /* pxa2xx I2S frame and clock master modes */ { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), .priv = 0x48, }, { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_11025, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_11025, .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), .priv = 0x34, }, { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_16000, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_16000, .pcmdir = PXA_I2S_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), + .fs = 256, + .bfs = SND_SOC_FSBD(4), .priv = 0x24, }, { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, .pcmrate = SNDRV_PCM_RATE_22050, .pcmdir = PXA_I2S_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), + .fs = 256, + .bfs = SND_SOC_FSBD(4), .priv = 0x1a, }, { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_44100, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_44100, .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), .priv = 0xd, }, { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, .bfs = SND_SOC_FSBD(4), .priv = 0xc, }, @@ -122,11 +122,11 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { /* pxa2xx I2S frame master and clock slave mode */ { .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, .pcmrate = PXA_I2S_RATES, .pcmdir = PXA_I2S_DIR, - .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB(64), + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSB(64), .priv = 0x48, }, }; -- cgit v1.2.3-59-g8ed1b From 3cee5a60ce18034a63f70ba2bdd54f85018ce960 Mon Sep 17 00:00:00 2001 From: Remy Bruno Date: Mon, 16 Oct 2006 12:46:32 +0200 Subject: [ALSA] hdspm: Add support for AES32 Add support for AES32. Difference between MADI and AES32 is done through revision. Master support is not finished for now (RME so-called DDS feature is not supported yet) Signed-off-by: Remy Bruno Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/rme9652/hdspm.c | 1320 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 1098 insertions(+), 222 deletions(-) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 0547f6f04bdc..3d3a4ce3a35e 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6,6 +6,8 @@ * code based on hdsp.c Paul Davis * Marcus Andersson * Thomas Charbonnel + * Modified 2006-06-01 for AES32 support by Remy Bruno + * * * 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 @@ -77,7 +79,8 @@ MODULE_PARM_DESC(enable_monitor, MODULE_AUTHOR ("Winfried Ritsch , Paul Davis , " - "Marcus Andersson, Thomas Charbonnel "); + "Marcus Andersson, Thomas Charbonnel , " + "Remy Bruno "); MODULE_DESCRIPTION("RME HDSPM"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); @@ -107,7 +110,12 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* --- Read registers. --- These are defined as byte-offsets from the iobase value */ #define HDSPM_statusRegister 0 -#define HDSPM_statusRegister2 96 +/*#define HDSPM_statusRegister2 96 */ +/* after RME Windows driver sources, status2 is 4-byte word # 48 = word at + * offset 192, for AES32 *and* MADI + * => need to check that offset 192 is working on MADI */ +#define HDSPM_statusRegister2 192 +#define HDSPM_timecodeRegister 128 #define HDSPM_midiDataIn0 360 #define HDSPM_midiDataIn1 364 @@ -140,37 +148,50 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Frequency0 (1<<6) /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */ #define HDSPM_Frequency1 (1<<7) /* 0=32kHz/64kHz */ #define HDSPM_DoubleSpeed (1<<8) /* 0=normal speed, 1=double speed */ -#define HDSPM_QuadSpeed (1<<31) /* quad speed bit, not implemented now */ +#define HDSPM_QuadSpeed (1<<31) /* quad speed bit */ +#define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */ #define HDSPM_TX_64ch (1<<10) /* Output 64channel MODE=1, - 56channelMODE=0 */ + 56channelMODE=0 */ /* MADI ONLY*/ +#define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */ #define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, - 0=off, 1=on */ + 0=off, 1=on */ /* MADI ONLY */ +#define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */ -#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ +#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ /* MADI ONLY*/ #define HDSPM_InputSelect1 (1<<15) /* should be 0 */ #define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ -#define HDSPM_SyncRef1 (1<<17) /* should be 0 */ +#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */ +#define HDSPM_SyncRef2 (1<<13) +#define HDSPM_SyncRef3 (1<<25) +#define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */ #define HDSPM_clr_tms (1<<19) /* clear track marker, do not use AES additional bits in lower 5 Audiodatabits ??? */ +#define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */ +#define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */ #define HDSPM_Midi0InterruptEnable (1<<22) #define HDSPM_Midi1InterruptEnable (1<<23) #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */ +#define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */ +#define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */ +#define HDSPM_QS_QuadWire (1<<28) /* AES32 ONLY */ + +#define HDSPM_wclk_sel (1<<30) /* --- bit helper defines */ #define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2) -#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1) +#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|HDSPM_DoubleSpeed|HDSPM_QuadSpeed) #define HDSPM_InputMask (HDSPM_InputSelect0|HDSPM_InputSelect1) #define HDSPM_InputOptical 0 #define HDSPM_InputCoaxial (HDSPM_InputSelect0) -#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1) +#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|HDSPM_SyncRef2|HDSPM_SyncRef3) #define HDSPM_SyncRef_Word 0 #define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) @@ -183,6 +204,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0) #define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1) #define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0) +#define HDSPM_Frequency128KHz (HDSPM_QuadSpeed|HDSPM_Frequency0) +#define HDSPM_Frequency176_4KHz (HDSPM_QuadSpeed|HDSPM_Frequency1) +#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|HDSPM_Frequency0) /* --- for internal discrimination */ #define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ @@ -229,7 +253,8 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_BIGENDIAN_MODE (1<<9) #define HDSPM_RD_MULTIPLE (1<<10) -/* --- Status Register bits --- */ +/* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and + that do not conflict with specific bits for AES32 seem to be valid also for the AES32 */ #define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */ #define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn. MODE=0 */ #define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 (like inp0) */ @@ -263,7 +288,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_madiFreq176_4 (HDSPM_madiFreq3) #define HDSPM_madiFreq192 (HDSPM_madiFreq3|HDSPM_madiFreq0) -/* Status2 Register bits */ +/* Status2 Register bits */ /* MADI ONLY */ #define HDSPM_version0 (1<<0) /* not realy defined but I guess */ #define HDSPM_version1 (1<<1) /* in former cards it was ??? */ @@ -297,6 +322,56 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) #define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2) +/* + For AES32, bits for status, status2 and timecode are different +*/ +/* status */ +#define HDSPM_AES32_wcLock 0x0200000 +#define HDSPM_AES32_wcFreq_bit 22 +/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function + HDSPM_bit2freq */ +#define HDSPM_AES32_syncref_bit 16 +/* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */ + +#define HDSPM_AES32_AUTOSYNC_FROM_WORD 0 +#define HDSPM_AES32_AUTOSYNC_FROM_AES1 1 +#define HDSPM_AES32_AUTOSYNC_FROM_AES2 2 +#define HDSPM_AES32_AUTOSYNC_FROM_AES3 3 +#define HDSPM_AES32_AUTOSYNC_FROM_AES4 4 +#define HDSPM_AES32_AUTOSYNC_FROM_AES5 5 +#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6 +#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7 +#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8 +#define HDSPM_AES32_AUTOSYNC_FROM_NONE -1 + +/* status2 */ +/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */ +#define HDSPM_LockAES 0x80 +#define HDSPM_LockAES1 0x80 +#define HDSPM_LockAES2 0x40 +#define HDSPM_LockAES3 0x20 +#define HDSPM_LockAES4 0x10 +#define HDSPM_LockAES5 0x8 +#define HDSPM_LockAES6 0x4 +#define HDSPM_LockAES7 0x2 +#define HDSPM_LockAES8 0x1 +/* + Timecode + After windows driver sources, bits 4*i to 4*i+3 give the input frequency on + AES i+1 + bits 3210 + 0001 32kHz + 0010 44.1kHz + 0011 48kHz + 0100 64kHz + 0101 88.2kHz + 0110 96kHz + 0111 128kHz + 1000 176.4kHz + 1001 192kHz + NB: Timecode register doesn't seem to work on AES32 card revision 230 +*/ + /* Mixer Values */ #define UNITY_GAIN 32768 /* = 65536/2 */ #define MINUS_INFINITY_GAIN 0 @@ -314,10 +389,14 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); size is the same regardless of the number of channels, and also the latency to use. for one direction !!! + => need to mupltiply by 2!! */ -#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) +#define HDSPM_DMA_AREA_BYTES (2 * HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024) +/* revisions >= 230 indicate AES32 card */ +#define HDSPM_AESREVISION 230 + struct hdspm_midi { struct hdspm *hdspm; int id; @@ -336,7 +415,9 @@ struct hdspm { struct snd_pcm_substream *playback_substream; /* and/or capture stream */ char *card_name; /* for procinfo */ - unsigned short firmware_rev; /* dont know if relevant */ + unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/ + + unsigned char is_aes32; /* indicates if card is AES32 */ int precise_ptr; /* use precise pointers, to be tested */ int monitor_outs; /* set up monitoring outs init flag */ @@ -453,6 +534,15 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm); static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, unsigned int reg, int channels); +static inline int HDSPM_bit2freq(int n) +{ + static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200, + 96000, 128000, 176400, 192000 }; + if (n < 1 || n > 9) + return 0; + return bit2freq_tab[n]; +} + /* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ @@ -544,86 +634,105 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) /* check for external sample rate */ static inline int hdspm_external_sample_rate(struct hdspm * hdspm) { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int rate_bits; - int rate = 0; + if (hdspm->is_aes32) { + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); + unsigned int timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + + int syncref = hdspm_autosync_ref(hdspm); + + if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && + status & HDSPM_AES32_wcLock) + return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); + if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && + syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && + status2 & (HDSPM_LockAES >> + (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) + return HDSPM_bit2freq((timecode >> + (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); + return 0; + } else { + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); + unsigned int rate_bits; + int rate = 0; - /* if wordclock has synced freq and wordclock is valid */ - if ((status2 & HDSPM_wcLock) != 0 && - (status & HDSPM_SelSyncRef0) == 0) { + /* if wordclock has synced freq and wordclock is valid */ + if ((status2 & HDSPM_wcLock) != 0 && + (status & HDSPM_SelSyncRef0) == 0) { - rate_bits = status2 & HDSPM_wcFreqMask; + rate_bits = status2 & HDSPM_wcFreqMask; - switch (rate_bits) { - case HDSPM_wcFreq32: - rate = 32000; - break; - case HDSPM_wcFreq44_1: - rate = 44100; - break; - case HDSPM_wcFreq48: - rate = 48000; - break; - case HDSPM_wcFreq64: - rate = 64000; - break; - case HDSPM_wcFreq88_2: - rate = 88200; - break; - case HDSPM_wcFreq96: - rate = 96000; - break; - /* Quadspeed Bit missing ???? */ - default: - rate = 0; - break; + switch (rate_bits) { + case HDSPM_wcFreq32: + rate = 32000; + break; + case HDSPM_wcFreq44_1: + rate = 44100; + break; + case HDSPM_wcFreq48: + rate = 48000; + break; + case HDSPM_wcFreq64: + rate = 64000; + break; + case HDSPM_wcFreq88_2: + rate = 88200; + break; + case HDSPM_wcFreq96: + rate = 96000; + break; + /* Quadspeed Bit missing ???? */ + default: + rate = 0; + break; + } } - } - /* if rate detected and Syncref is Word than have it, word has priority to MADI */ - if (rate != 0 - && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) - return rate; + /* if rate detected and Syncref is Word than have it, word has priority to MADI */ + if (rate != 0 && + (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + return rate; - /* maby a madi input (which is taken if sel sync is madi) */ - if (status & HDSPM_madiLock) { - rate_bits = status & HDSPM_madiFreqMask; + /* maby a madi input (which is taken if sel sync is madi) */ + if (status & HDSPM_madiLock) { + rate_bits = status & HDSPM_madiFreqMask; - switch (rate_bits) { - case HDSPM_madiFreq32: - rate = 32000; - break; - case HDSPM_madiFreq44_1: - rate = 44100; - break; - case HDSPM_madiFreq48: - rate = 48000; - break; - case HDSPM_madiFreq64: - rate = 64000; - break; - case HDSPM_madiFreq88_2: - rate = 88200; - break; - case HDSPM_madiFreq96: - rate = 96000; - break; - case HDSPM_madiFreq128: - rate = 128000; - break; - case HDSPM_madiFreq176_4: - rate = 176400; - break; - case HDSPM_madiFreq192: - rate = 192000; - break; - default: - rate = 0; - break; + switch (rate_bits) { + case HDSPM_madiFreq32: + rate = 32000; + break; + case HDSPM_madiFreq44_1: + rate = 44100; + break; + case HDSPM_madiFreq48: + rate = 48000; + break; + case HDSPM_madiFreq64: + rate = 64000; + break; + case HDSPM_madiFreq88_2: + rate = 88200; + break; + case HDSPM_madiFreq96: + rate = 96000; + break; + case HDSPM_madiFreq128: + rate = 128000; + break; + case HDSPM_madiFreq176_4: + rate = 176400; + break; + case HDSPM_madiFreq192: + rate = 192000; + break; + default: + rate = 0; + break; + } } + return rate; } - return rate; } /* Latency function */ @@ -676,7 +785,8 @@ static inline void hdspm_silence_playback(struct hdspm * hdspm) int n = hdspm->period_bytes; void *buf = hdspm->playback_buffer; - snd_assert(buf != NULL, return); + if (buf == NULL) + return; for (i = 0; i < HDSPM_MAX_CHANNELS; i++) { memset(buf, 0, n); @@ -716,6 +826,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) int current_rate; int rate_bits; int not_set = 0; + int is_single, is_double, is_quad; /* ASSUMPTION: hdspm->lock is either set, or there is no need for it (e.g. during module initialization). @@ -766,43 +877,56 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) changes in the read/write routines. */ + is_single = (current_rate <= 48000); + is_double = (current_rate > 48000 && current_rate <= 96000); + is_quad = (current_rate > 96000); + switch (rate) { case 32000: - if (current_rate > 48000) { + if (!is_single) reject_if_open = 1; - } rate_bits = HDSPM_Frequency32KHz; break; case 44100: - if (current_rate > 48000) { + if (!is_single) reject_if_open = 1; - } rate_bits = HDSPM_Frequency44_1KHz; break; case 48000: - if (current_rate > 48000) { + if (!is_single) reject_if_open = 1; - } rate_bits = HDSPM_Frequency48KHz; break; case 64000: - if (current_rate <= 48000) { + if (!is_double) reject_if_open = 1; - } rate_bits = HDSPM_Frequency64KHz; break; case 88200: - if (current_rate <= 48000) { + if (!is_double) reject_if_open = 1; - } rate_bits = HDSPM_Frequency88_2KHz; break; case 96000: - if (current_rate <= 48000) { + if (!is_double) reject_if_open = 1; - } rate_bits = HDSPM_Frequency96KHz; break; + case 128000: + if (!is_quad) + reject_if_open = 1; + rate_bits = HDSPM_Frequency128KHz; + break; + case 176400: + if (!is_quad) + reject_if_open = 1; + rate_bits = HDSPM_Frequency176_4KHz; + break; + case 192000: + if (!is_quad) + reject_if_open = 1; + rate_bits = HDSPM_Frequency192KHz; + break; default: return -EINVAL; } @@ -819,7 +943,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) hdspm->control_register |= rate_bits; hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); - if (rate > 64000) + if (rate > 96000 /* 64000*/) hdspm->channel_map = channel_map_madi_qs; else if (rate > 48000) hdspm->channel_map = channel_map_madi_ds; @@ -1455,11 +1579,27 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm) /* Notice that this looks at the requested sync source, not the one actually in use. */ - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - return HDSPM_SYNC_FROM_WORD; - case HDSPM_SyncRef_MADI: - return HDSPM_SYNC_FROM_MADI; + if (hdspm->is_aes32) { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + /* number gives AES index, except for 0 which + corresponds to WordClock */ + case 0: return 0; + case HDSPM_SyncRef0: return 1; + case HDSPM_SyncRef1: return 2; + case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; + case HDSPM_SyncRef2: return 4; + case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; + case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; + case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7; + case HDSPM_SyncRef3: return 8; + } + } else { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case HDSPM_SyncRef_Word: + return HDSPM_SYNC_FROM_WORD; + case HDSPM_SyncRef_MADI: + return HDSPM_SYNC_FROM_MADI; + } } return HDSPM_SYNC_FROM_WORD; @@ -1469,15 +1609,49 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { hdspm->control_register &= ~HDSPM_SyncRefMask; - switch (pref) { - case HDSPM_SYNC_FROM_MADI: - hdspm->control_register |= HDSPM_SyncRef_MADI; - break; - case HDSPM_SYNC_FROM_WORD: - hdspm->control_register |= HDSPM_SyncRef_Word; - break; - default: - return -1; + if (hdspm->is_aes32) { + switch (pref) { + case 0: + hdspm->control_register |= 0; + break; + case 1: + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: + hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 4: + hdspm->control_register |= HDSPM_SyncRef2; + break; + case 5: + hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0; + break; + case 6: + hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1; + break; + case 7: + hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 8: + hdspm->control_register |= HDSPM_SyncRef3; + break; + default: + return -1; + } + } else { + switch (pref) { + case HDSPM_SYNC_FROM_MADI: + hdspm->control_register |= HDSPM_SyncRef_MADI; + break; + case HDSPM_SYNC_FROM_WORD: + hdspm->control_register |= HDSPM_SyncRef_Word; + break; + default: + return -1; + } } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); return 0; @@ -1486,18 +1660,36 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Word", "MADI" }; + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + if (hdspm->is_aes32) { + static char *texts[] = { "Word", "AES1", "AES2", "AES3", + "AES4", "AES5", "AES6", "AES7", "AES8" }; - uinfo->value.enumerated.items = 2; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; - 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]); + uinfo->value.enumerated.items = 9; + + 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]); + } else { + static char *texts[] = { "Word", "MADI" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + } return 0; } @@ -1517,7 +1709,7 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, int change, max; unsigned int val; - max = 2; + max = hdspm->is_aes32 ? 9 : 2; if (!snd_hdspm_use_is_exclusive(hdspm)) return -EBUSY; @@ -1542,40 +1734,64 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, static int hdspm_autosync_ref(struct hdspm * hdspm) { - /* This looks at the autosync selected sync reference */ - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - - switch (status2 & HDSPM_SelSyncRefMask) { - - case HDSPM_SelSyncRef_WORD: - return HDSPM_AUTOSYNC_FROM_WORD; - - case HDSPM_SelSyncRef_MADI: - return HDSPM_AUTOSYNC_FROM_MADI; - - case HDSPM_SelSyncRef_NVALID: - return HDSPM_AUTOSYNC_FROM_NONE; + if (hdspm->is_aes32) { + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); + unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF; + if (syncref == 0) + return HDSPM_AES32_AUTOSYNC_FROM_WORD; + if (syncref <= 8) + return syncref; + return HDSPM_AES32_AUTOSYNC_FROM_NONE; + } else { + /* This looks at the autosync selected sync reference */ + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + switch (status2 & HDSPM_SelSyncRefMask) { + case HDSPM_SelSyncRef_WORD: + return HDSPM_AUTOSYNC_FROM_WORD; + case HDSPM_SelSyncRef_MADI: + return HDSPM_AUTOSYNC_FROM_MADI; + case HDSPM_SelSyncRef_NVALID: + return HDSPM_AUTOSYNC_FROM_NONE; + default: + return 0; + } - default: return 0; } - - return 0; } static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "WordClock", "MADI", "None" }; + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + if (hdspm->is_aes32) { + static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", + "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 10; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + } + else + { + static char *texts[] = { "WordClock", "MADI", "None" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + } return 0; } @@ -1787,45 +2003,376 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol, .put = snd_hdspm_put_safe_mode \ } -static int hdspm_safe_mode(struct hdspm * hdspm) +static int hdspm_safe_mode(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0; +} + +static int hdspm_set_safe_mode(struct hdspm * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_AutoInp; + else + hdspm->control_register &= ~HDSPM_AutoInp; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_safe_mode(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 snd_hdspm_get_safe_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_safe_mode(hdspm); + hdspm_set_safe_mode(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_EMPHASIS(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_emphasis, \ + .get = snd_hdspm_get_emphasis, \ + .put = snd_hdspm_put_emphasis \ +} + +static int hdspm_emphasis(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0; +} + +static int hdspm_set_emphasis(struct hdspm * hdspm, int emp) +{ + if (emp) + hdspm->control_register |= HDSPM_Emphasis; + else + hdspm->control_register &= ~HDSPM_Emphasis; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_emphasis(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 snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_emphasis(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_emphasis(hdspm); + hdspm_set_emphasis(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_DOLBY(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_dolby, \ + .get = snd_hdspm_get_dolby, \ + .put = snd_hdspm_put_dolby \ +} + +static int hdspm_dolby(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_Dolby) ? 1 : 0; +} + +static int hdspm_set_dolby(struct hdspm * hdspm, int dol) +{ + if (dol) + hdspm->control_register |= HDSPM_Dolby; + else + hdspm->control_register &= ~HDSPM_Dolby; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_dolby(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 snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_dolby(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_dolby(hdspm); + hdspm_set_dolby(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_PROFESSIONAL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_professional, \ + .get = snd_hdspm_get_professional, \ + .put = snd_hdspm_put_professional \ +} + +static int hdspm_professional(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_Professional) ? 1 : 0; +} + +static int hdspm_set_professional(struct hdspm * hdspm, int dol) +{ + if (dol) + hdspm->control_register |= HDSPM_Professional; + else + hdspm->control_register &= ~HDSPM_Professional; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_professional(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 snd_hdspm_get_professional(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_professional(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_professional(hdspm); + hdspm_set_professional(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_INPUT_SELECT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_input_select, \ + .get = snd_hdspm_get_input_select, \ + .put = snd_hdspm_put_input_select \ +} + +static int hdspm_input_select(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0; +} + +static int hdspm_set_input_select(struct hdspm * hdspm, int out) +{ + if (out) + hdspm->control_register |= HDSPM_InputSelect0; + else + hdspm->control_register &= ~HDSPM_InputSelect0; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "optical", "coaxial" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdspm->lock); + change = (int) val != hdspm_input_select(hdspm); + hdspm_set_input_select(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_DS_WIRE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_ds_wire, \ + .get = snd_hdspm_get_ds_wire, \ + .put = snd_hdspm_put_ds_wire \ +} + +static int hdspm_ds_wire(struct hdspm * hdspm) { - return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0; + return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0; } -static int hdspm_set_safe_mode(struct hdspm * hdspm, int out) +static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds) { - if (out) - hdspm->control_register |= HDSPM_AutoInp; + if (ds) + hdspm->control_register |= HDSPM_DS_DoubleWire; else - hdspm->control_register &= ~HDSPM_AutoInp; + hdspm->control_register &= ~HDSPM_DS_DoubleWire; hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); return 0; } -static int snd_hdspm_info_safe_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + static char *texts[] = { "Single", "Double" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } -static int snd_hdspm_get_safe_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); spin_lock_irq(&hdspm->lock); - ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm); + ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm); spin_unlock_irq(&hdspm->lock); return 0; } -static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int change; @@ -1835,45 +2382,56 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, return -EBUSY; val = ucontrol->value.integer.value[0] & 1; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_safe_mode(hdspm); - hdspm_set_safe_mode(hdspm, val); + change = (int) val != hdspm_ds_wire(hdspm); + hdspm_set_ds_wire(hdspm, val); spin_unlock_irq(&hdspm->lock); return change; } -#define HDSPM_INPUT_SELECT(xname, xindex) \ +#define HDSPM_QS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ - .info = snd_hdspm_info_input_select, \ - .get = snd_hdspm_get_input_select, \ - .put = snd_hdspm_put_input_select \ + .info = snd_hdspm_info_qs_wire, \ + .get = snd_hdspm_get_qs_wire, \ + .put = snd_hdspm_put_qs_wire \ } -static int hdspm_input_select(struct hdspm * hdspm) +static int hdspm_qs_wire(struct hdspm * hdspm) { - return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0; + if (hdspm->control_register & HDSPM_QS_DoubleWire) + return 1; + if (hdspm->control_register & HDSPM_QS_QuadWire) + return 2; + return 0; } -static int hdspm_set_input_select(struct hdspm * hdspm, int out) +static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode) { - if (out) - hdspm->control_register |= HDSPM_InputSelect0; - else - hdspm->control_register &= ~HDSPM_InputSelect0; + hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire); + switch (mode) { + case 0: + break; + case 1: + hdspm->control_register |= HDSPM_QS_DoubleWire; + break; + case 2: + hdspm->control_register |= HDSPM_QS_QuadWire; + break; + } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); return 0; } -static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol, +static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "optical", "coaxial" }; + static char *texts[] = { "Single", "Double", "Quad" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 2; + uinfo->value.enumerated.items = 3; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = @@ -1884,30 +2442,34 @@ static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol, return 0; } -static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol, +static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); spin_lock_irq(&hdspm->lock); - ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm); + ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm); spin_unlock_irq(&hdspm->lock); return 0; } -static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, +static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int change; - unsigned int val; + int val; if (!snd_hdspm_use_is_exclusive(hdspm)) return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; + val = ucontrol->value.integer.value[0]; + if (val < 0) + val = 0; + if (val > 2) + val = 2; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_input_select(hdspm); - hdspm_set_input_select(hdspm, val); + change = (int) val != hdspm_qs_wire(hdspm); + hdspm_set_qs_wire(hdspm, val); spin_unlock_irq(&hdspm->lock); return change; } @@ -2135,14 +2697,24 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, static int hdspm_wc_sync_check(struct hdspm * hdspm) { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - if (status2 & HDSPM_wcLock) { - if (status2 & HDSPM_wcSync) + if (hdspm->is_aes32) { + int status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_AES32_wcLock) { + /* I don't know how to differenciate sync from lock. + Doing as if sync for now */ return 2; - else - return 1; + } + return 0; + } else { + int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + if (status2 & HDSPM_wcLock) { + if (status2 & HDSPM_wcSync) + return 2; + else + return 1; + } + return 0; } - return 0; } static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol, @@ -2188,9 +2760,43 @@ static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol, } +#define HDSPM_AES_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_aes_sync_check \ +} + +static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx) +{ + int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + if (status2 & (HDSPM_LockAES >> idx)) { + /* I don't know how to differenciate sync from lock. + Doing as if sync for now */ + return 2; + } + return 0; +} + +static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int offset; + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + offset = ucontrol->id.index - 1; + if (offset < 0 || offset >= 8) + return -EINVAL; + + ucontrol->value.enumerated.item[0] = + hdspm_aes_sync_check(hdspm, offset); + return 0; +} -static struct snd_kcontrol_new snd_hdspm_controls[] = { +static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { HDSPM_MIXER("Mixer", 0), /* 'Sample Clock Source' complies with the alsa control naming scheme */ @@ -2211,6 +2817,29 @@ static struct snd_kcontrol_new snd_hdspm_controls[] = { HDSPM_INPUT_SELECT("Input Select", 0), }; +static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { + + HDSPM_MIXER("Mixer", 0), +/* 'Sample Clock Source' complies with the alsa control naming scheme */ + HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), + + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), + HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), +/* 'External Rate' complies with the alsa control naming scheme */ + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), +/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */ + HDSPM_LINE_OUT("Line Out", 0), + HDSPM_EMPHASIS("Emphasis", 0), + HDSPM_DOLBY("Non Audio", 0), + HDSPM_PROFESSIONAL("Professional", 0), + HDSPM_C_TMS("Clear Track Marker", 0), + HDSPM_DS_WIRE("Double Speed Wire Mode", 0), + HDSPM_QS_WIRE("Quad Speed Wire Mode", 0), +}; + static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER; @@ -2245,20 +2874,40 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm struct snd_kcontrol *kctl; /* add control list first */ - - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls); idx++) { - if ((err = - snd_ctl_add(card, kctl = - snd_ctl_new1(&snd_hdspm_controls[idx], - hdspm))) < 0) { - return err; + if (hdspm->is_aes32) { + struct snd_kcontrol_new aes_sync_ctl = + HDSPM_AES_SYNC_CHECK("AES Lock Status", 0); + + for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32); + idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_hdspm_controls_aes32[idx], + hdspm)); + if (err < 0) + return err; + } + for (idx = 1; idx <= 8; idx++) { + aes_sync_ctl.index = idx; + err = snd_ctl_add(card, + snd_ctl_new1(&aes_sync_ctl, hdspm)); + if (err < 0) + return err; + } + } else { + for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi); + idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_hdspm_controls_madi[idx], + hdspm)); + if (err < 0) + return err; } } /* Channel playback mixer as default control - Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer - they are accesible via special IOCTL on hwdep - and the mixer 2dimensional mixer control */ +Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer +they are accesible via special IOCTL on hwdep +and the mixer 2dimensional mixer control */ snd_hdspm_playback_mixer.name = "Chn"; limit = HDSPM_MAX_CHANNELS; @@ -2289,7 +2938,8 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm ------------------------------------------------------------*/ static void -snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) +snd_hdspm_proc_read_madi(struct snd_info_entry * entry, + struct snd_info_buffer *buffer) { struct hdspm *hdspm = (struct hdspm *) entry->private_data; unsigned int status; @@ -2420,11 +3070,10 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe clock_source = "Error"; } snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); - if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { + if (!(hdspm->control_register & HDSPM_ClockModeMaster)) system_clock_mode = "Slave"; - } else { + else system_clock_mode = "Master"; - } snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); switch (hdspm_pref_sync_ref(hdspm)) { @@ -2484,13 +3133,213 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe snd_iprintf(buffer, "\n"); } +static void +snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = (struct hdspm *) entry->private_data; + unsigned int status; + unsigned int status2; + unsigned int timecode; + int pref_syncref; + char *autosync_ref; + char *system_clock_mode; + char *clock_source; + int x; + + status = hdspm_read(hdspm, HDSPM_statusRegister); + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + + snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n", + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev); + + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + + snd_iprintf(buffer, "--- System ---\n"); + + snd_iprintf(buffer, + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); + snd_iprintf(buffer, + "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % (2 * + (int)hdspm-> + period_bytes), + ((status & HDSPM_BufferPositionMask) - + 64) % (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); + + snd_iprintf(buffer, + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, status2=0x%x, timecode=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2, timecode); + + snd_iprintf(buffer, "--- Settings ---\n"); + + x = 1 << (6 + + hdspm_decode_latency(hdspm-> + control_register & + HDSPM_LatencyMask)); + + snd_iprintf(buffer, + "Size (Latency): %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) hdspm->period_bytes); + + snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", + (hdspm-> + control_register & HDSPM_LineOut) ? "on " : "off", + (hdspm->precise_ptr) ? "on" : "off"); + + snd_iprintf(buffer, + "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", + (hdspm-> + control_register & HDSPM_clr_tms) ? "on" : "off", + (hdspm-> + control_register & HDSPM_Emphasis) ? "on" : "off", + (hdspm-> + control_register & HDSPM_Dolby) ? "on" : "off"); + + switch (hdspm_clock_source(hdspm)) { + case HDSPM_CLOCK_SOURCE_AUTOSYNC: + clock_source = "AutoSync"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: + clock_source = "Internal 32 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: + clock_source = "Internal 44.1 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: + clock_source = "Internal 48 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: + clock_source = "Internal 64 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: + clock_source = "Internal 88.2 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: + clock_source = "Internal 96 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: + clock_source = "Internal 128 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: + clock_source = "Internal 176.4 kHz"; + break; + case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: + clock_source = "Internal 192 kHz"; + break; + default: + clock_source = "Error"; + } + snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); + if (!(hdspm->control_register & HDSPM_ClockModeMaster)) + system_clock_mode = "Slave"; + else + system_clock_mode = "Master"; + snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); + + pref_syncref = hdspm_pref_sync_ref(hdspm); + if (pref_syncref == 0) + snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n"); + else + snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n", + pref_syncref); + + snd_iprintf(buffer, "System Clock Frequency: %d\n", + hdspm->system_sample_rate); + + snd_iprintf(buffer, "Double speed: %s\n", + hdspm->control_register & HDSPM_DS_DoubleWire? + "Double wire" : "Single wire"); + snd_iprintf(buffer, "Quad speed: %s\n", + hdspm->control_register & HDSPM_QS_DoubleWire? + "Double wire" : + hdspm->control_register & HDSPM_QS_QuadWire? + "Quad wire" : "Single wire"); + + snd_iprintf(buffer, "--- Status:\n"); + + snd_iprintf(buffer, "Word: %s Frequency: %d\n", + (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock", + HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF)); + + for (x = 0; x < 8; x++) { + snd_iprintf(buffer, "AES%d: %s Frequency: %d\n", + x+1, + (status2 & (HDSPM_LockAES >> x))? "Sync ": "No Lock", + HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); + } + + switch (hdspm_autosync_ref(hdspm)) { + case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break; + case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break; + default: autosync_ref = "---"; break; + } + snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); + + snd_iprintf(buffer, "\n"); +} + +#ifdef CONFIG_SND_DEBUG +static void +snd_hdspm_proc_read_debug(struct snd_info_entry * entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = (struct hdspm *)entry->private_data; + + int j,i; + + for (i = 0; i < 256 /* 1024*64 */; i += j) + { + snd_iprintf(buffer, "0x%08X: ", i); + for (j = 0; j < 16; j += 4) + snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j)); + snd_iprintf(buffer, "\n"); + } +} +#endif + + + static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) { struct snd_info_entry *entry; if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read); + hdspm->is_aes32 ? + snd_hdspm_proc_read_aes32 : + snd_hdspm_proc_read_madi); +#ifdef CONFIG_SND_DEBUG + /* debug file to read all hdspm registers */ + if (!snd_card_proc_new(hdspm->card, "debug", &entry)) + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_debug); +#endif } /*------------------------------------------------------------ @@ -2507,13 +3356,20 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) /* set defaults: */ - hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ - HDSPM_InputCoaxial | /* Input Coax not Optical */ - HDSPM_SyncRef_MADI | /* Madi is syncclock */ - HDSPM_LineOut | /* Analog output in */ - HDSPM_TX_64ch | /* transmit in 64ch mode */ - HDSPM_AutoInp; /* AutoInput chossing (takeover) */ + if (hdspm->is_aes32) + hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ + HDSPM_SyncRef0 | /* AES1 is syncclock */ + HDSPM_LineOut | /* Analog output in */ + HDSPM_Professional; /* Professional mode */ + else + hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ + HDSPM_InputCoaxial | /* Input Coax not Optical */ + HDSPM_SyncRef_MADI | /* Madi is syncclock */ + HDSPM_LineOut | /* Analog output in */ + HDSPM_TX_64ch | /* transmit in 64ch mode */ + HDSPM_AutoInp; /* AutoInput chossing (takeover) */ /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ @@ -2822,6 +3678,8 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, hdspm->playback_buffer = (unsigned char *) substream->runtime->dma_area; + snd_printdd("Allocated sample buffer for playback at 0x%08X\n", + hdspm->playback_buffer); } else { hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn, params_channels(params)); @@ -2831,7 +3689,15 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, hdspm->capture_buffer = (unsigned char *) substream->runtime->dma_area; + snd_printdd("Allocated sample buffer for capture at 0x%08X\n", + hdspm->capture_buffer); } + /* + snd_printdd("Allocated sample buffer for %s at 0x%08X\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture", + snd_pcm_sgbuf_get_addr(sgbuf, 0)); + */ return 0; } @@ -2982,9 +3848,10 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ), .rate_min = 32000, - .rate_max = 96000, + .rate_max = 192000, .channels_min = 1, .channels_max = HDSPM_MAX_CHANNELS, .buffer_bytes_max = @@ -3006,9 +3873,10 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = { SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000), .rate_min = 32000, - .rate_max = 96000, + .rate_max = 192000, .channels_min = 1, .channels_max = HDSPM_MAX_CHANNELS, .buffer_bytes_max = @@ -3315,7 +4183,8 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) pcm = hdspm->pcm; - wanted = HDSPM_DMA_AREA_BYTES + 4096; /* dont know why, but it works */ +/* wanted = HDSPM_DMA_AREA_BYTES + 4096;*/ /* dont know why, but it works */ + wanted = HDSPM_DMA_AREA_BYTES; if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, @@ -3467,9 +4336,16 @@ static int __devinit snd_hdspm_create(struct snd_card *card, struct hdspm * hdsp pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); - strcpy(card->driver, "HDSPM"); + hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION); + strcpy(card->mixername, "Xilinx FPGA"); - hdspm->card_name = "RME HDSPM MADI"; + if (hdspm->is_aes32) { + strcpy(card->driver, "HDSPAES32"); + hdspm->card_name = "RME HDSPM AES32"; + } else { + strcpy(card->driver, "HDSPM"); + hdspm->card_name = "RME HDSPM MADI"; + } if ((err = pci_enable_device(pci)) < 0) return err; -- cgit v1.2.3-59-g8ed1b From cbcc2c4c07bd34586c7fd8d7513d3a397d39ce3c Mon Sep 17 00:00:00 2001 From: Jerome Demange Date: Mon, 16 Oct 2006 21:08:57 +0200 Subject: [ALSA] ac97 - enables sound output through speakers on MSI S250 laptop Signed-off-by: Jerome Demange Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index e813968e0cf8..123de550d1f4 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -2264,7 +2264,8 @@ int patch_alc655(struct snd_ac97 * ac97) if (ac97->subsystem_vendor == 0x1462 && (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */ ac97->subsystem_device == 0x0161 || /* LG K1 Express */ - ac97->subsystem_device == 0x0351)) /* MSI L725 laptop */ + ac97->subsystem_device == 0x0351 || /* MSI L725 laptop */ + ac97->subsystem_device == 0x0061)) /* MSI S250 laptop */ val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */ else val |= (1 << 1); /* Pin 47 is spdif input pin */ -- cgit v1.2.3-59-g8ed1b From 12e74f7d430655f541b85018ea62bcd669094bd7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 16 Oct 2006 21:19:48 +0200 Subject: [ALSA] ASoC - Fix build warnings in soc-core.c This patch fixes some build warnings in soc-core.c Changes:- o Check the return value of soc_ac97_dev_register() o Check return value of calls to device_create_file() Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 8d6ff047d7a0..2ce0c8251dc3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1438,12 +1438,18 @@ int snd_soc_register_card(struct snd_soc_device *socdev) { struct snd_soc_codec *codec = socdev->codec; struct snd_soc_machine *machine = socdev->machine; - int ret = 0, i, ac97 = 0; + int ret = 0, i, ac97 = 0, err = 0; mutex_lock(&codec->mutex); for(i = 0; i < machine->num_links; i++) { - if (socdev->machine->dai_link[i].init) - socdev->machine->dai_link[i].init(codec); + if (socdev->machine->dai_link[i].init) { + err = socdev->machine->dai_link[i].init(codec); + if (err < 0) { + printk(KERN_ERR "asoc: failed to init %s\n", + socdev->machine->dai_link[i].stream_name); + continue; + } + } if (socdev->machine->dai_link[i].cpu_dai->type == SND_SOC_DAI_AC97) ac97 = 1; } @@ -1456,17 +1462,28 @@ int snd_soc_register_card(struct snd_soc_device *socdev) if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n", codec->name); - mutex_unlock(&codec->mutex); - return ret; + goto out; } #ifdef CONFIG_SND_SOC_AC97_BUS - if (ac97) - soc_ac97_dev_register(codec); + if (ac97) { + ret = soc_ac97_dev_register(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: AC97 device register failed\n"); + snd_card_free(codec->card); + goto out; + } + } #endif - snd_soc_dapm_sys_add(socdev->dev); - device_create_file(socdev->dev, &dev_attr_codec_reg); + err = snd_soc_dapm_sys_add(socdev->dev); + if (err < 0) + printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); + + err = device_create_file(socdev->dev, &dev_attr_codec_reg); + if (err < 0) + printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); +out: mutex_unlock(&codec->mutex); return ret; } -- cgit v1.2.3-59-g8ed1b From a53d1aece388d940831846f642810e47526883e8 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Tue, 17 Oct 2006 12:00:28 +0200 Subject: [ALSA] hda-codec - Add toshiba model to ALC861 codec This patch adds support for Toshiba laptops. Code is from RealTek's alsa-driver-1.0.12-4.05b tree. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 77 ++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 08f63ee53742..a16dd34817d2 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -826,6 +826,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 6stack-dig 6-jack with SPDIF I/O 3stack-660 3-jack (for ALC660) uniwill-m31 Uniwill M31 laptop + toshiba Toshiba laptop support auto auto-config reading BIOS (default) CMI9880 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1420db43423a..62c75388457e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -91,6 +91,7 @@ enum { ALC861_3ST_DIG, ALC861_6ST_DIG, ALC861_UNIWILL_M31, + ALC861_TOSHIBA, ALC861_AUTO, ALC861_MODEL_LAST, }; @@ -6206,7 +6207,29 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = { .private_value = ARRAY_SIZE(alc861_threestack_modes), }, { } /* end */ -}; +}; + +static snd_kcontrol_new_t alc861_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + + /*Capture mixer control */ + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + + { } /* end */ +}; + static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { /* output mixer control */ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), @@ -6489,6 +6512,39 @@ static struct hda_verb alc861_auto_init_verbs[] = { { } }; +static struct hda_verb alc861_toshiba_init_verbs[] = { + {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc861_toshiba_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x0f, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_update(codec, 0x16, 0, HDA_INPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x16, 1, HDA_INPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_INPUT, 3, + 0x80, present ? 0 : 0x80); + snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_INPUT, 3, + 0x80, present ? 0 : 0x80); +} + +static void alc861_toshiba_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 6bit tag is placed at 26 bit! + */ + if ((res >> 26) == ALC880_HP_EVENT) + alc861_toshiba_automute(codec); +} + /* pcm configuration: identiacal with ALC880 */ #define alc861_pcm_analog_playback alc880_pcm_analog_playback #define alc861_pcm_analog_capture alc880_pcm_analog_capture @@ -6774,6 +6830,11 @@ static struct hda_board_config alc861_cfg_tbl[] = { { .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31}, { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072, .config = ALC861_UNIWILL_M31 }, + { .modelname = "toshiba", .config = ALC861_TOSHIBA }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1338, + .config = ALC861_TOSHIBA }, + { .pci_subvendor = 0x1179, .pci_subdevice = 0xff10, + .config = ALC861_TOSHIBA }, { .modelname = "auto", .config = ALC861_AUTO }, {} }; @@ -6841,7 +6902,19 @@ static struct alc_config_preset alc861_presets[] = { .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, }, - + [ALC861_TOSHIBA] = { + .mixers = { alc861_toshiba_mixer }, + .init_verbs = { alc861_base_init_verbs, alc861_toshiba_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + .unsol_event = alc861_toshiba_unsol_event, + .init_hook = alc861_toshiba_automute, + }, }; -- cgit v1.2.3-59-g8ed1b From ccc656ce5f6627032bd44e660071bb71e65a231a Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 17 Oct 2006 12:32:26 +0200 Subject: [ALSA] hda-codec - Add new modesl for Realtek codecs Changes from Realtek driver: - New models hippo and hippo_1 for ALC262 - New models tagra-dig and tagra-2ch-dig for ALC883 - New id for ALC660 codec chip Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 4 + sound/pci/hda/patch_realtek.c | 536 +++++++++++++++++++++++- 2 files changed, 534 insertions(+), 6 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index a16dd34817d2..feb97bd711f9 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -801,6 +801,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. fujitsu Fujitsu Laptop hp-bpc HP xw4400/6400/8400/9400 laptops benq Benq ED8 + hippo Hippo (ATI) with jack detection + hippo_1 Hippo (Benq) with jack detection basic fixed pin assignment w/o SPDIF auto auto-config reading BIOS (default) @@ -818,6 +820,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 6stack-dig-demo 6-jack digital for Intel demo board acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc) medion Medion Laptops + targa-dig Targa/MSI + targa-2ch-dig Targs/MSI with 2-channel auto auto-config reading BIOS (default) ALC861/660 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 62c75388457e..04749d2f7bdf 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -32,6 +32,10 @@ #include "hda_codec.h" #include "hda_local.h" +#define ALC880_FRONT_EVENT 0x01 +#define ALC880_DCVOL_EVENT 0x02 +#define ALC880_HP_EVENT 0x04 +#define ALC880_MIC_EVENT 0x08 /* ALC880 board config type */ enum { @@ -49,6 +53,8 @@ enum { ALC880_ASUS_W1V, ALC880_ASUS_DIG2, ALC880_UNIWILL_DIG, + ALC880_UNIWILL, + ALC880_UNIWILL_P53, ALC880_CLEVO, ALC880_TCL_S700, ALC880_LG, @@ -77,6 +83,8 @@ enum { /* ALC262 models */ enum { ALC262_BASIC, + ALC262_HIPPO, + ALC262_HIPPO_1, ALC262_FUJITSU, ALC262_HP_BPC, ALC262_BENQ_ED8, @@ -111,6 +119,8 @@ enum { ALC883_3ST_6ch_DIG, ALC883_3ST_6ch, ALC883_6ST_DIG, + ALC883_TARGA_DIG, + ALC883_TARGA_2ch_DIG, ALC888_DEMO_BOARD, ALC883_ACER, ALC883_MEDION, @@ -1017,6 +1027,46 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { { } /* end */ }; +/* Uniwill */ +static struct snd_kcontrol_new alc880_uniwill_mixer[] = { + HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { + HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + /* * build control elements */ @@ -1250,6 +1300,154 @@ static struct hda_verb alc880_pin_6stack_init_verbs[] = { { } }; +/* + * Uniwill pin configuration: + * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19, + * line = 0x1a + */ +static struct hda_verb alc880_uniwill_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */ + /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + + { } +}; + +/* +* Uniwill P53 +* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, + */ +static struct hda_verb alc880_uniwill_p53_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc880_uniwill_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x16, 0, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x16, 1, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_write(codec, 0x0b, 0, AC_VERB_SET_AMP_GAIN_MUTE, + 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); +} + +static void alc880_uniwill_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 4bit tag is placed at 28 bit! + */ + if ((res >> 28) == ALC880_HP_EVENT || + (res >> 28) == ALC880_MIC_EVENT) + alc880_uniwill_automute(codec); +} + +static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + snd_hda_codec_amp_update(codec, 0x15, 0, HDA_INPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x15, 1, HDA_INPUT, 0, + 0x80, present ? 0x80 : 0); +} + +static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x21, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0) & 0x7f; + + snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0, + 0x7f, present); + snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0, + 0x7f, present); + + snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0, + 0x7f, present); + snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0, + 0x7f, present); + +} +static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 4bit tag is placed at 28 bit! + */ + if ((res >> 28) == ALC880_HP_EVENT) + alc880_uniwill_p53_hp_automute(codec); + if ((res >> 28) == ALC880_DCVOL_EVENT) + alc880_uniwill_p53_dcvol_automute(codec); +} + /* FIXME! */ /* * F1734 pin configuration: @@ -2262,7 +2460,10 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 }, { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG }, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG }, + { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG }, + { .pci_subvendor = 0x1584, .pci_subdevice = 0x9070, .config = ALC880_UNIWILL }, + { .pci_subvendor = 0x1734, .pci_subdevice = 0x10ac, .config = ALC880_UNIWILL }, + { .pci_subvendor = 0x1584, .pci_subdevice = 0x9077, .config = ALC880_UNIWILL_P53 }, { .modelname = "F1734", .config = ALC880_F1734 }, { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 }, @@ -2440,7 +2641,8 @@ static struct alc_config_preset alc880_presets[] = { }, [ALC880_UNIWILL_DIG] = { .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer }, - .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), .dac_nids = alc880_asus_dac_nids, .dig_out_nid = ALC880_DIGOUT_NID, @@ -2449,6 +2651,32 @@ static struct alc_config_preset alc880_presets[] = { .need_dac_fix = 1, .input_mux = &alc880_capture_source, }, + [ALC880_UNIWILL] = { + .mixers = { alc880_uniwill_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_unsol_event, + .init_hook = alc880_uniwill_automute, + }, + [ALC880_UNIWILL_P53] = { + .mixers = { alc880_uniwill_p53_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_p53_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), + .channel_mode = alc880_w810_modes, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .init_hook = alc880_uniwill_p53_hp_automute, + }, [ALC880_CLEVO] = { .mixers = { alc880_three_stack_mixer }, .init_verbs = { alc880_volume_init_verbs, @@ -4912,6 +5140,62 @@ static snd_kcontrol_new_t alc883_fivestack_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_tagra_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -5000,6 +5284,45 @@ static struct hda_verb alc883_init_verbs[] = { { } }; +static struct hda_verb alc883_tagra_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_tagra_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, present ? 1 : 3); +} + +static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc883_tagra_automute(codec); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -5101,9 +5424,9 @@ static struct hda_board_config alc883_cfg_tbl[] = { .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ { .modelname = "3stack-6ch", .config = ALC883_3ST_6ch }, { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, - .config = ALC883_3ST_6ch }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601, - .config = ALC883_3ST_6ch }, /* D102GGC */ + .config = ALC883_3ST_6ch }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601, + .config = ALC883_3ST_6ch }, /* D102GGC */ { .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC883_6ST_DIG }, /* MSI */ @@ -5111,6 +5434,32 @@ static struct hda_board_config alc883_cfg_tbl[] = { .config = ALC883_6ST_DIG }, /* MSI K9A Platinum (MS-7280) */ { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC883_6ST_DIG }, /* Foxconn */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x7187, + .config = ALC883_6ST_DIG }, /* MSI */ + { .modelname = "targa-dig", .config = ALC883_TARGA_DIG }, + { .pci_subvendor = 0x1462, .pci_subdevice = 0x4314, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fcc, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fc1, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fc3, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x4314, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x4319, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x3ef9, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x4324, + .config = ALC883_TARGA_DIG }, /* MSI */ + { .modelname = "targa-2ch-dig", .config = ALC883_TARGA_2ch_DIG }, + { .pci_subvendor = 0x1462, .pci_subdevice = 0x0579, + .config = ALC883_TARGA_2ch_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0xa422, + .config = ALC883_TARGA_2ch_DIG }, /* MSI */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x3b7f, + .config = ALC883_TARGA_2ch_DIG }, /* MSI */ { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, { .modelname = "acer", .config = ALC883_ACER }, { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/, @@ -5178,6 +5527,35 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, }, + [ALC883_TARGA_DIG] = { + .mixers = { alc883_tagra_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_tagra_unsol_event, + .init_hook = alc883_tagra_automute, + }, + [ALC883_TARGA_2ch_DIG] = { + .mixers = { alc883_tagra_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_tagra_unsol_event, + .init_hook = alc883_tagra_automute, + }, [ALC888_DEMO_BOARD] = { .mixers = { alc883_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs }, @@ -5408,6 +5786,24 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc262_hippo1_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */ + /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/ + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), @@ -5512,6 +5908,103 @@ static struct hda_verb alc262_init_verbs[] = { { } }; +static struct hda_verb alc262_hippo_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_verb alc262_hippo1_unsol_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hippo_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || ! spec->sense_updated) { + 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 & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, + 0x80, 0x80); + snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, + 0x80, 0x80); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + mute = snd_hda_codec_amp_read(codec, 0x15, 1, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_hippo_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hippo_automute(codec, 1); +} + +static void alc262_hippo1_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || ! spec->sense_updated) { + unsigned int present; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, + 0x80, 0x80); + snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, + 0x80, 0x80); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + mute = snd_hda_codec_amp_read(codec, 0x1b, 1, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_hippo1_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hippo1_automute(codec, 1); +} + /* * fujitsu model * 0x14 = headphone/spdif-out, 0x15 = internal speaker @@ -5921,6 +6414,12 @@ static void alc262_auto_init(struct hda_codec *codec) */ static struct hda_board_config alc262_cfg_tbl[] = { { .modelname = "basic", .config = ALC262_BASIC }, + { .modelname = "hippo", + .pci_subvendor =0x1002, .pci_subdevice = 0x437b, + .config = ALC262_HIPPO}, + { .modelname = "hippo_1", + .pci_subvendor =0x17ff, .pci_subdevice = 0x058f, + .config = ALC262_HIPPO_1}, { .modelname = "fujitsu", .config = ALC262_FUJITSU }, { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, @@ -5953,6 +6452,30 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, }, + [ALC262_HIPPO] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo_unsol_event, + }, + [ALC262_HIPPO_1] = { + .mixers = { alc262_hippo1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo1_unsol_event, + }, [ALC262_FUJITSU] = { .mixers = { alc262_fujitsu_mixer }, .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs }, @@ -6983,7 +7506,8 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861", .patch = patch_alc861 }, - { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", + { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, + { .id = 0x10ec0660, .name = "ALC660", .patch = patch_alc861 }, {} /* terminator */ }; -- cgit v1.2.3-59-g8ed1b From d7923b2a816625dc4208d89471da6bdcab188cdb Mon Sep 17 00:00:00 2001 From: Remy Bruno Date: Tue, 17 Oct 2006 12:41:56 +0200 Subject: [ALSA] hdsp - Add DDS register support for RME9632 rev >= 152 Add DDS register support for RME9632 rev >= 152. This register sets the sample rate for these cards and is required in addition to the standard control register. It corresponds to a quartz divisor. Signed-off-by: Remy Bruno Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/rme9652/hdsp.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 849ffe4aa5ca..89b3c7ff5037 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -80,6 +80,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP}," /* Write registers. These are defined as byte-offsets from the iobase value. */ #define HDSP_resetPointer 0 +#define HDSP_freqReg 0 #define HDSP_outputBufferAddress 32 #define HDSP_inputBufferAddress 36 #define HDSP_controlRegister 64 @@ -469,6 +470,7 @@ struct hdsp { struct pci_dev *pci; struct snd_kcontrol *spdif_ctl; unsigned short mixer_matrix[HDSP_MATRIX_MIXER_SIZE]; + unsigned int dds_value; /* last value written to freq register */ }; /* These tables map the ALSA channels 1..N to the channels that we @@ -940,6 +942,11 @@ static snd_pcm_uframes_t hdsp_hw_pointer(struct hdsp *hdsp) static void hdsp_reset_hw_pointer(struct hdsp *hdsp) { hdsp_write (hdsp, HDSP_resetPointer, 0); + if (hdsp->io_type == H9632 && hdsp->firmware_rev >= 152) + /* HDSP_resetPointer = HDSP_freqReg, which is strange and + * requires (?) to write again DDS value after a reset pointer + * (at least, it works like this) */ + hdsp_write (hdsp, HDSP_freqReg, hdsp->dds_value); } static void hdsp_start_audio(struct hdsp *s) @@ -984,6 +991,30 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames) return 0; } +static void hdsp_set_dds_value(struct hdsp *hdsp, int rate) +{ + u64 n; + u32 r; + + if (rate >= 112000) + rate /= 4; + else if (rate >= 56000) + rate /= 2; + + /* RME says n = 104857600000000, but in the windows MADI driver, I see: +// return 104857600000000 / rate; // 100 MHz + return 110100480000000 / rate; // 105 MHz + */ + n = 104857600000000ULL; /* = 2^20 * 10^8 */ + div64_32(&n, rate, &r); + /* n should be less than 2^32 for being written to FREQ register */ + snd_assert((n >> 32) == 0); + /* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS + value to write it after a reset */ + hdsp->dds_value = n; + hdsp_write(hdsp, HDSP_freqReg, hdsp->dds_value); +} + static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally) { int reject_if_open = 0; @@ -1092,6 +1123,10 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally) hdsp->control_register |= rate_bits; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + /* For HDSP9632 rev 152, need to set DDS value in FREQ register */ + if (hdsp->io_type == H9632 && hdsp->firmware_rev >= 152) + hdsp_set_dds_value(hdsp, rate); + if (rate >= 128000) { hdsp->channel_map = channel_map_H9632_qs; } else if (rate > 48000) { @@ -4945,6 +4980,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card, hdsp->irq = pci->irq; hdsp->precise_ptr = 0; hdsp->use_midi_tasklet = 1; + hdsp->dds_value = 0; if ((err = snd_hdsp_initialize_memory(hdsp)) < 0) return err; -- cgit v1.2.3-59-g8ed1b From 543a0fbe18d0b44f3d037fe6b59458fa0c0d5e4b Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Thu, 19 Oct 2006 18:22:53 +0200 Subject: [ALSA] ASoC AT91 DAI modes update This patch by Frank Mandarino updates the AT91RM9200 I2S DAI audio modes as follows:- o fixes a typo in the 16k mode o removes experimental 24k mode o adds a 32k mode. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c index 8c4d3b999053..2eee427b1e5c 100644 --- a/sound/soc/at91/at91rm9200-i2s.c +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -71,22 +71,22 @@ static struct snd_soc_dai_mode at91rm9200_i2s[] = { .flags = SND_SOC_DAI_BFS_DIV, .fs = 750, .bfs = SND_SOC_FSBD(3), - .flags (7 << 16 | 133), + .priv = (7 << 16 | 133), }, - /* 24k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ + /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ { .fmt = AT91RM9200_I2S_DAIFMT, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_22050, + .pcmrate = SNDRV_PCM_RATE_32000, .pcmdir = AT91RM9200_I2S_DIR, .flags = SND_SOC_DAI_BFS_DIV, - .fs = 500, - .bfs = SND_SOC_FSBD(10), - .priv = (25 << 16 | 24), + .fs = 375, + .bfs = SND_SOC_FSBD(3), + .priv = (7 << 16 | 66), }, - /* 48kHz: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ + /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ { .fmt = AT91RM9200_I2S_DAIFMT, .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, -- cgit v1.2.3-59-g8ed1b From a71a468a50f1385855e28864e26251b02df829bb Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 19 Oct 2006 20:35:56 +0200 Subject: [ALSA] ASoC: Add support for BCLK based on (Rate * Chn * Word Size) This patch adds support for the DAI BCLK to be generated by multiplying Rate * Channels * Word Size (RCW). This now gives 3 options for BCLK clocking and synchronisation :- 1. BCLK = Rate * x 2. BCLK = MCLK / x 3. BCLK = Rate * Chn * Word Size. (New) Changes:- o Add support for RCW generation of BCLK o Update Documentation to include RCW. o Update DAI documentation for label = value DAI modes. o Add RCW support to wm8731, wm8750 and pxa2xx-i2s drivers. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/soc/DAI.txt | 358 ++++++++++++++++++++++-------- Documentation/sound/alsa/soc/clocking.txt | 13 +- include/sound/soc.h | 23 +- sound/soc/codecs/wm8731.c | 33 ++- sound/soc/codecs/wm8750.c | 15 +- sound/soc/pxa/pxa2xx-i2s.c | 3 +- sound/soc/soc-core.c | 215 ++++++++++++++---- 7 files changed, 481 insertions(+), 179 deletions(-) diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/alsa/soc/DAI.txt index 919de76bab8d..251545a88693 100644 --- a/Documentation/sound/alsa/soc/DAI.txt +++ b/Documentation/sound/alsa/soc/DAI.txt @@ -12,7 +12,8 @@ The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97 frame is 21uS long and is divided into 13 time slots. -The AC97 specification can be found at http://intel.com/ +The AC97 specification can be found at :- +http://www.intel.com/design/chipsets/audio/ac97_r23.pdf I2S @@ -77,16 +78,16 @@ sample rates first and then test your interface. struct snd_soc_dai_mode is defined (in soc.h) as:- /* SoC DAI mode */ -struct snd_soc_hw_mode { - unsigned int fmt:16; /* SND_SOC_DAIFMT_* */ - unsigned int tdm:16; /* SND_SOC_DAITDM_* */ - unsigned int pcmfmt:6; /* SNDRV_PCM_FORMAT_* */ - unsigned int pcmrate:16; /* SND_SOC_DAIRATE_* */ - unsigned int pcmdir:2; /* SND_SOC_DAIDIR_* */ - unsigned int flags:8; /* hw flags */ - unsigned int fs:32; /* mclk to rate dividers */ - unsigned int bfs:16; /* mclk to bclk dividers */ - unsigned long priv; /* private mode data */ +struct snd_soc_dai_mode { + u16 fmt; /* SND_SOC_DAIFMT_* */ + u16 tdm; /* SND_SOC_HWTDM_* */ + u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */ + u16 pcmrate; /* SND_SOC_HWRATE_* */ + u16 pcmdir:2; /* SND_SOC_HWDIR_* */ + u16 flags:8; /* hw flags */ + u16 fs; /* mclk to rate divider */ + u64 bfs; /* mclk to bclk dividers */ + unsigned long priv; /* private mode data */ }; fmt: @@ -140,14 +141,14 @@ pcmfmt: The hardware PCM format. This describes the PCM formats supported by the DAI mode e.g. - .hwpcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ SNDRV_PCM_FORMAT_S24_3LE pcmrate: ---------- The PCM sample rates supported by the DAI mode. e.g. - .hwpcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 @@ -161,9 +162,14 @@ flags: -------- The DAI hardware flags supported by the mode. -SND_SOC_DAI_BFS_DIV -This flag states that bit clock is generated by dividing MCLK in this mode, if -this flag is absent the bitclock generated by mulitiplying sample rate. +/* use bfs mclk divider mode (BCLK = MCLK / x) */ +#define SND_SOC_DAI_BFS_DIV 0x1 +/* use bfs rate mulitplier (BCLK = RATE * x)*/ +#define SND_SOC_DAI_BFS_RATE 0x2 +/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */ +#define SND_SOC_DAI_BFS_RCW 0x4 +/* capture and playback can use different clocks */ +#define SND_SOC_DAI_ASYNC 0x8 NOTE: Bitclock division and mulitiplication modes can be safely matched by the core logic. @@ -181,7 +187,7 @@ depends on the codec or CPU DAI). The BFS supported by the DAI mode. This can either be the ratio between the bitclock (BCLK) and the sample rate OR the ratio between the system clock and -the sample rate. Depends on the SND_SOC_DAI_BFS_DIV flag above. +the sample rate. Depends on the flags above. priv: ----- @@ -207,10 +213,15 @@ Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a BCLK of either MCLK/2 or MCLK/4. /* codec master */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 256, SND_SOC_FSBD(2) | SND_SOC_FSBD(4)}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(2) | SND_SOC_FSBD(4), + } Example 2 @@ -219,32 +230,95 @@ Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a BCLK of either Rate * 32 or Rate * 64. /* codec master */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, - 256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = 256, + .bfs = 32, + }, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = 256, + .bfs = 64, + }, Example 3 --------- +Codec that runs at 8k & 48k @ 256FS in master mode, can generate a BCLK that +is a multiple of Rate * channels * word size. (RCW) i.e. + + BCLK = 8000 * 2 * 16 (8k, stereo, 16bit) + = 256kHz + +This codecs supports a RCW multiple of 1,2 + + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RCW, + .fs = 256, + .bfs = SND_SOC_FSBW(1) | SND_SOC_FSBW(2), + } + + +Example 4 +--------- Codec that only runs at 8k & 48k @ 256FS in master mode, can generate a BCLK of either Rate * 32 or Rate * 64. Codec can also run in slave mode as long as BCLK is rate * 32 or rate * 64. /* codec master */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, - 256, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = 256, + .bfs = 32, + }, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = 256, + .bfs = 64, + }, /* codec slave */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, - SND_SOC_FS_ALL, SND_SOC_FSB(32) | SND_SOC_FSB(64)}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = SND_SOC_FS_ALL, + .bfs = 32, + }, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_RATE, + .fs = SND_SOC_FS_ALL, + .bfs = 64, + }, -Example 4 +Example 5 --------- Codec that only runs at 8k, 16k, 32k, 48k, 96k @ 128FS, 192FS & 256FS in master mode and can generate a BCLK of MCLK / (1,2,4,8,16). Codec can also run in slave @@ -259,29 +333,48 @@ mode as and does not care about FS or BCLK (as long as there is enough bandwidth SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) /* codec master @ 128, 192 & 256 FS */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 128, CODEC_FSB}, - - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 192, CODEC_FSB}, - - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 256, CODEC_FSB}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = CODEC_RATES, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 128, + .bfs = CODEC_FSB, + }, + + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = CODEC_RATES, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 192, + .bfs = CODEC_FSB + }, + + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = CODEC_RATES, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = CODEC_FSB, + }, /* codec slave */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, - SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = CODEC_RATES, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSB_ALL, + }, -Example 5 +Example 6 --------- Codec that only runs at 8k, 44.1k, 48k @ different FS in master mode (for use with a fixed MCLK) and can generate a BCLK of MCLK / (1,2,4,8,16). @@ -298,45 +391,66 @@ sizes. SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE) /* codec master */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_8000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 1536, CODEC_FSB}, - - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_44100, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 272, CODEC_FSB}, - - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_RATE_48000, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, SND_SOC_DAI_BFS_DIV, - 256, CODEC_FSB}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1536, + .bfs = CODEC_FSB, + }, + + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_44100, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 272, + .bfs = CODEC_FSB, + }, + + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = CODEC_FSB, + }, /* codec slave */ - {SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), - SNDRV_PCM_FORMAT_S16_LE, CODEC_RATES, - SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, 0, - SND_SOC_FS_ALL, SND_SOC_FSB_ALL}, + { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, + .pcmrate = CODEC_RATES, + .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, + .fs = SND_SOC_FS_ALL, + .bfs = SND_SOC_FSB_ALL, + }, -Example 6 +Example 7 --------- AC97 Codec that does not support VRA (i.e only runs at 48k). #define AC97_DIR \ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - #define AC97_PCM_FORMATS \ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \ SNDRV_PCM_FORMAT_S20_3LE) /* AC97 with no VRA */ - {0, 0, AC97_PCM_FORMATS, SNDRV_PCM_RATE_48000}, + { + .pcmfmt = AC97_PCM_FORMATS, + .pcmrate = SNDRV_PCM_RATE_48000, + } -Example 7 +Example 8 --------- CPU DAI that supports 8k - 48k @ 256FS and BCLK = MCLK / 4 in master mode. @@ -354,27 +468,79 @@ BCLK = 64 * rate. (Intel XScale I2S controller). SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + /* priv is divider */ + static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { /* pxa2xx I2S frame and clock master modes */ - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_8000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0x48}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_11025, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0x34}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_16000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0x24}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_22050, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0x1a}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_44100, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0xd}, - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_RATE_48000, PXA_I2S_DIR, SND_SOC_DAI_BFS_DIV, 256, - SND_SOC_FSBD(4), 0xc}, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x48, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_11025, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x34, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x24, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_22050, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0x1a, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_44100, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0xd, + }, + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = PXA_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 256, + .bfs = SND_SOC_FSBD(4), + .priv = 0xc, + }, /* pxa2xx I2S frame master and clock slave mode */ - {PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE, - PXA_I2S_RATES, PXA_I2S_DIR, 0, SND_SOC_FS_ALL, SND_SOC_FSB(64)}, - + { + .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = PXA_I2S_RATES, + .pcmdir = PXA_I2S_DIR, + .fs = SND_SOC_FS_ALL, + .flags = SND_SOC_DAI_BFS_RATE, + .bfs = 64, + .priv = 0x48, + }, +}; diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/alsa/soc/clocking.txt index 88a16c9e1979..1f55fd8cb117 100644 --- a/Documentation/sound/alsa/soc/clocking.txt +++ b/Documentation/sound/alsa/soc/clocking.txt @@ -26,9 +26,9 @@ between the codec and CPU. The DAI also has a frame clock to signal the start of each audio frame. This clock is sometimes referred to as LRC (left right clock) or FRAME. This clock -runs at exactly the sample rate. +runs at exactly the sample rate (LRC = Rate). -Bit Clock is usually always a ratio of MCLK or a multiple of LRC. i.e. +Bit Clock can be generated as follows:- BCLK = MCLK / x @@ -36,9 +36,14 @@ BCLK = MCLK / x BCLK = LRC * x + or + +BCLK = LRC * Channels * Word Size + This relationship depends on the codec or SoC CPU in particular. ASoC can quite -easily match a codec that generates BCLK by division (FSBD) with a CPU that -generates BCLK by multiplication (FSB). +easily match BCLK generated by division (SND_SOC_DAI_BFS_DIV) with BCLK by +multiplication (SND_SOC_DAI_BFS_RATE) or BCLK generated by +Rate * Channels * Word size (RCW or SND_SOC_DAI_BFS_RCW). ASoC Clocking diff --git a/include/sound/soc.h b/include/sound/soc.h index ecdd1fac94b6..3dfe052e0788 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -21,7 +21,7 @@ #include #include -#define SND_SOC_VERSION "0.11.8" +#define SND_SOC_VERSION "0.12" /* * Convenience kcontrol builders @@ -141,19 +141,24 @@ /* bit clock dividers */ #define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */ #define SND_SOC_FSBD_REAL(x) (ffs(x)) -#define SND_SOC_FSBD_ALL 0xffff /* all bit clock dividers supported */ -/* bit clock ratio to sample rate */ -#define SND_SOC_FSB(x) (1 << ((x - 16) / 16)) -#define SND_SOC_FSB_REAL(x) (((ffs(x) - 1) * 16) + 16) +/* bit clock ratio to (sample rate * channels * word size) */ +#define SND_SOC_FSBW(x) (1 << (x - 1)) +#define SND_SOC_FSBW_REAL(x) (ffs(x)) /* all bclk ratios supported */ -#define SND_SOC_FSB_ALL SND_SOC_FSBD_ALL +#define SND_SOC_FSB_ALL ~0ULL /* * DAI hardware flags */ -/* use bfs mclk divider mode, else sample rate ratio */ -#define SND_SOC_DAI_BFS_DIV 0x1 +/* use bfs mclk divider mode (BCLK = MCLK / x) */ +#define SND_SOC_DAI_BFS_DIV 0x1 +/* use bfs rate mulitplier (BCLK = RATE * x)*/ +#define SND_SOC_DAI_BFS_RATE 0x2 +/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */ +#define SND_SOC_DAI_BFS_RCW 0x4 +/* capture and playback can use different clocks */ +#define SND_SOC_DAI_ASYNC 0x8 /* * AC97 codec ID's bitmask @@ -264,7 +269,7 @@ struct snd_soc_dai_mode { u16 pcmdir:2; /* SND_SOC_HWDIR_* */ u16 flags:8; /* hw flags */ u16 fs; /* mclk to rate divider */ - u32 bfs; /* mclk to bclk dividers */ + u64 bfs; /* mclk to bclk dividers */ unsigned long priv; /* private mode data */ }; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9adbd2d401c4..412291241ece 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -90,32 +90,36 @@ static struct snd_soc_dai_mode wm8731_modes[] = { .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 1536, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 2304, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 1408, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_8000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 2112, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, /* 32k */ @@ -124,16 +128,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = { .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 384, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_32000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 576, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, /* 44.1k & 48k */ @@ -142,16 +148,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = { .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 256, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 384, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, /* 88.2 & 96k */ @@ -160,17 +168,18 @@ static struct snd_soc_dai_mode wm8731_modes[] = { .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 128, - .bfs = SND_SOC_FSB(64), - + .bfs = 64, }, { .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, .pcmfmt = WM8731_HIFI_BITS, .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .pcmdir = WM8731_DIR, + .flags = SND_SOC_DAI_BFS_RATE, .fs = 192, - .bfs = SND_SOC_FSB(64), + .bfs = 64, }, /* USB codec frame and clock master modes */ @@ -237,7 +246,7 @@ static struct snd_soc_dai_mode wm8731_modes[] = { .pcmdir = WM8731_DIR, .flags = SND_SOC_DAI_BFS_DIV, .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSBD_ALL, + .bfs = SND_SOC_FSB_ALL, }, }; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 243da712d9c1..c5d13a9454d9 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -343,7 +343,7 @@ static struct snd_soc_dai_mode wm8750_modes[] = { .pcmdir = WM8750_DIR, .flags = SND_SOC_DAI_BFS_DIV, .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSBD_ALL, + .bfs = SND_SOC_FSB_ALL, }, }; @@ -829,6 +829,9 @@ static inline int get_coeff(int mclk, int rate) if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) return i; } + + printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n", + mclk, rate); return -EINVAL; } @@ -836,13 +839,7 @@ static inline int get_coeff(int mclk, int rate) static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai, struct snd_soc_clock_info *info, unsigned int clk) { - dai->mclk = 0; - - /* check that the calculated FS and rate actually match a clock from - * the machine driver */ - if (info->fs * info->rate == clk) - dai->mclk = clk; - + dai->mclk = clk; return dai->mclk; } @@ -859,7 +856,7 @@ static int wm8750_pcm_prepare(struct snd_pcm_substream *substream) if (i < 0) return i; - bfs = SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs); + bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs); /* set master/slave audio interface */ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 99f1da32744b..98b167fe68e5 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -126,7 +126,8 @@ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { .pcmrate = PXA_I2S_RATES, .pcmdir = PXA_I2S_DIR, .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB(64), + .flags = SND_SOC_DAI_BFS_RATE, + .bfs = 64, .priv = 0x48, }, }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2ce0c8251dc3..6da1616bf776 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -51,6 +51,8 @@ #define dbgc(format, arg...) #endif +#define CODEC_CPU(codec, cpu) ((codec << 4) | cpu) + static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(io_mutex); static struct workqueue_struct *soc_workq; @@ -150,11 +152,11 @@ static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd, } /* changes a bitclk multiplier mask to a divider mask */ -static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk, +static u64 soc_bfs_rcw_to_div(u64 bfs, int rate, unsigned int mclk, unsigned int pcmfmt, unsigned int chn) { int i, j; - u16 bfs_ = 0; + u64 bfs_ = 0; int size = snd_pcm_format_physical_width(pcmfmt), min = 0; if (size <= 0) @@ -162,17 +164,14 @@ static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk, /* the minimum bit clock that has enough bandwidth */ min = size * rate * chn; - dbgc("mult --> div min bclk %d with mclk %d\n", min, mclk); + dbgc("rcw --> div min bclk %d with mclk %d\n", min, mclk); - for (i = 0; i < 16; i++) { + for (i = 0; i < 64; i++) { if ((bfs >> i) & 0x1) { - j = rate * SND_SOC_FSB_REAL(1<= min) { - bfs_ |= SND_SOC_FSBD(mclk/j); - dbgc("mult --> div support mult %d\n", - SND_SOC_FSB_REAL(1< div support mult %d\n", + SND_SOC_FSBD_REAL(1<> i) & 0x1) { - j = mclk / (SND_SOC_FSBD_REAL(1<= min) { - bfs_ |= SND_SOC_FSB(j/rate); - dbgc("div --> mult support div %d\n", - SND_SOC_FSBD_REAL(1< rcw support div %d\n", + SND_SOC_FSBW_REAL(1< rcw min bclk %d with mclk %d\n", min, mclk); + + if (bfs_ < min) + return 0; + else { + bfs_ = SND_SOC_FSBW(bfs_/min); + dbgc("rate --> rcw support div %d\n", SND_SOC_FSBW_REAL(bfs_)); + return bfs_; + } +} + +/* changes a bitclk multiplier mask to a divider mask */ +static u64 soc_bfs_rate_to_div(u64 bfs, int rate, unsigned int mclk, + unsigned int pcmfmt, unsigned int chn) +{ + unsigned int bfs_ = rate * bfs; + int size = snd_pcm_format_physical_width(pcmfmt), min = 0; + + if (size <= 0) + return 0; + + /* the minimum bit clock that has enough bandwidth */ + min = size * rate * chn; + dbgc("rate --> div min bclk %d with mclk %d\n", min, mclk); + + if (bfs_ < min) + return 0; + else { + bfs_ = SND_SOC_FSBW(mclk/bfs_); + dbgc("rate --> div support div %d\n", SND_SOC_FSBD_REAL(bfs_)); + return bfs_; + } +} + /* Matches codec DAI and SoC CPU DAI hardware parameters */ static int soc_hw_match_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -217,9 +262,10 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream, struct snd_soc_dai_mode *codec_dai_mode = NULL; struct snd_soc_dai_mode *cpu_dai_mode = NULL; struct snd_soc_clock_info clk_info; - unsigned int fs, mclk, codec_bfs, cpu_bfs, rate = params_rate(params), + unsigned int fs, mclk, rate = params_rate(params), chn, j, k, cpu_bclk, codec_bclk, pcmrate; u16 fmt = 0; + u64 codec_bfs, cpu_bfs; dbg("asoc: match version %s\n", SND_SOC_VERSION); clk_info.rate = rate; @@ -309,44 +355,98 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream, * used in the codec and cpu DAI modes. We always choose the * lowest possible clocks to reduce power. */ - if (codec_dai_mode->flags & cpu_dai_mode->flags & - SND_SOC_DAI_BFS_DIV) { + switch (CODEC_CPU(codec_dai_mode->flags, cpu_dai_mode->flags)) { + case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_DIV): /* cpu & codec bfs dividers */ rtd->cpu_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs = 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1); - } else if (codec_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { - /* normalise bfs codec divider & cpu mult */ - codec_bfs = soc_bfs_div_to_mult(codec_dai_mode->bfs, rate, + break; + case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RCW): + /* normalise bfs codec divider & cpu rcw mult */ + codec_bfs = soc_bfs_div_to_rcw(codec_dai_mode->bfs, rate, mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); rtd->cpu_dai->dai_runtime.bfs = 1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1); - cpu_bfs = soc_bfs_mult_to_div(cpu_dai_mode->bfs, rate, mclk, + cpu_bfs = soc_bfs_rcw_to_div(cpu_dai_mode->bfs, rate, mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); rtd->codec_dai->dai_runtime.bfs = 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1); - } else if (cpu_dai_mode->flags & SND_SOC_DAI_BFS_DIV) { - /* normalise bfs codec mult & cpu divider */ - codec_bfs = soc_bfs_mult_to_div(codec_dai_mode->bfs, rate, + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_DIV): + /* normalise bfs codec rcw mult & cpu divider */ + codec_bfs = soc_bfs_rcw_to_div(codec_dai_mode->bfs, rate, mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); rtd->cpu_dai->dai_runtime.bfs = 1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1); - cpu_bfs = soc_bfs_div_to_mult(cpu_dai_mode->bfs, rate, mclk, + cpu_bfs = soc_bfs_div_to_rcw(cpu_dai_mode->bfs, rate, mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); rtd->codec_dai->dai_runtime.bfs = 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1); - } else { - /* codec & cpu bfs rate multipliers */ + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RCW): + /* codec & cpu bfs rate rcw multipliers */ rtd->cpu_dai->dai_runtime.bfs = rtd->codec_dai->dai_runtime.bfs = 1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1); + break; + case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RATE): + /* normalise cpu bfs rate const multiplier & codec div */ + cpu_bfs = soc_bfs_rate_to_div(cpu_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + if(codec_dai_mode->bfs & cpu_bfs) { + rtd->codec_dai->dai_runtime.bfs = cpu_bfs; + rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs; + } else + rtd->cpu_dai->dai_runtime.bfs = 0; + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RATE): + /* normalise cpu bfs rate const multiplier & codec rcw mult */ + cpu_bfs = soc_bfs_rate_to_rcw(cpu_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + if(codec_dai_mode->bfs & cpu_bfs) { + rtd->codec_dai->dai_runtime.bfs = cpu_bfs; + rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs; + } else + rtd->cpu_dai->dai_runtime.bfs = 0; + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RCW): + /* normalise cpu bfs rate rcw multiplier & codec const mult */ + codec_bfs = soc_bfs_rate_to_rcw(codec_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + if(cpu_dai_mode->bfs & codec_bfs) { + rtd->cpu_dai->dai_runtime.bfs = codec_bfs; + rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs; + } else + rtd->cpu_dai->dai_runtime.bfs = 0; + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_DIV): + /* normalise cpu bfs div & codec const mult */ + codec_bfs = soc_bfs_rate_to_div(codec_dai_mode->bfs, rate, + mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); + if(codec_dai_mode->bfs & codec_bfs) { + rtd->cpu_dai->dai_runtime.bfs = codec_bfs; + rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs; + } else + rtd->cpu_dai->dai_runtime.bfs = 0; + break; + case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RATE): + /* cpu & codec constant mult */ + if(codec_dai_mode->bfs == cpu_dai_mode->bfs) + rtd->cpu_dai->dai_runtime.bfs = + rtd->codec_dai->dai_runtime.bfs = + codec_dai_mode->bfs; + else + rtd->cpu_dai->dai_runtime.bfs = + rtd->codec_dai->dai_runtime.bfs = 0; + break; } /* make sure the bit clock speed is acceptable */ if (!rtd->cpu_dai->dai_runtime.bfs || !rtd->codec_dai->dai_runtime.bfs) { dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k); - dbgc("asoc: cpu_dai %x codec %x\n", + dbgc("asoc: cpu_dai %llu codec %llu\n", rtd->cpu_dai->dai_runtime.bfs, rtd->codec_dai->dai_runtime.bfs); dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt); @@ -378,26 +478,41 @@ found: dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n", rtd->codec_dai->dai_runtime.fs, mclk, SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); - } else { - codec_bclk = params_rate(params) * - SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs); - dbg("asoc: codec fs %d mclk %d bfs mult %d bclk %d\n", + } else if(rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) { + codec_bclk = params_rate(params) * rtd->codec_dai->dai_runtime.bfs; + dbg("asoc: codec fs %d mclk %d bfs rate mult %llu bclk %d\n", rtd->codec_dai->dai_runtime.fs, mclk, - SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); - } + rtd->codec_dai->dai_runtime.bfs, codec_bclk); + } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) { + codec_bclk = params_rate(params) * params_channels(params) * + snd_pcm_format_physical_width(rtd->codec_dai->dai_runtime.pcmfmt) * + SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs); + dbg("asoc: codec fs %d mclk %d bfs rcw mult %d bclk %d\n", + rtd->codec_dai->dai_runtime.fs, mclk, + SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); + } else + codec_bclk = 0; + if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) { cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) / SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs); dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n", rtd->cpu_dai->dai_runtime.fs, mclk, SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); - } else { - cpu_bclk = params_rate(params) * - SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs); - dbg("asoc: cpu fs %d mclk %d bfs mult %d bclk %d\n", + } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) { + cpu_bclk = params_rate(params) * rtd->cpu_dai->dai_runtime.bfs; + dbg("asoc: cpu fs %d mclk %d bfs rate mult %llu bclk %d\n", rtd->cpu_dai->dai_runtime.fs, mclk, - SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); - } + rtd->cpu_dai->dai_runtime.bfs, cpu_bclk); + } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) { + cpu_bclk = params_rate(params) * params_channels(params) * + snd_pcm_format_physical_width(rtd->cpu_dai->dai_runtime.pcmfmt) * + SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs); + dbg("asoc: cpu fs %d mclk %d bfs mult rcw %d bclk %d\n", + rtd->cpu_dai->dai_runtime.fs, mclk, + SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); + } else + cpu_bclk = 0; /* * Check we have matching bitclocks. If we don't then it means the @@ -405,7 +520,7 @@ found: * machine sysclock function) is wrong compared with the supported DAI * modes for the codec or cpu DAI. */ - if (cpu_bclk != codec_bclk){ + if (cpu_bclk != codec_bclk && cpu_bclk){ printk(KERN_ERR "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n" ); @@ -723,14 +838,18 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) mutex_lock(&pcm_mutex); if (platform->pcm_ops->prepare) { ret = platform->pcm_ops->prepare(substream); - if (ret < 0) + if (ret < 0) { + printk(KERN_ERR "asoc: platform prepare error\n"); goto out; + } } if (rtd->codec_dai->ops.prepare) { ret = rtd->codec_dai->ops.prepare(substream); - if (ret < 0) + if (ret < 0) { + printk(KERN_ERR "asoc: codec DAI prepare error\n"); goto out; + } } if (rtd->cpu_dai->ops.prepare) -- cgit v1.2.3-59-g8ed1b From 7cdbff945e9e3bb592dee2f66afbcc2255747f8f Mon Sep 17 00:00:00 2001 From: Mariusz Domanski Date: Mon, 23 Oct 2006 13:42:56 +0200 Subject: [ALSA] hda-codec - Add asus model to ALC861 codec This patch adds support for Asus laptops (for example: Asus A6Rp-AP002). Signed-off-by: Mariusz Domanski Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 165 +++++++++++++++++++++++- 2 files changed, 164 insertions(+), 2 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index feb97bd711f9..e4255f69b300 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -831,6 +831,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack-660 3-jack (for ALC660) uniwill-m31 Uniwill M31 laptop toshiba Toshiba laptop support + asus Asus laptop support auto auto-config reading BIOS (default) CMI9880 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04749d2f7bdf..990714e2bcb3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -100,6 +100,7 @@ enum { ALC861_6ST_DIG, ALC861_UNIWILL_M31, ALC861_TOSHIBA, + ALC861_ASUS, ALC861_AUTO, ALC861_MODEL_LAST, }; @@ -6654,6 +6655,44 @@ static struct hda_channel_mode alc861_uniwill_m31_modes[2] = { { 4, alc861_uniwill_m31_ch4_init }, }; +/* Set mic1 and line-in as input and unmute the mixer */ +static struct hda_verb alc861_asus_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif + { } /* end */ +}; +/* Set mic1 nad line-in as output and mute mixer */ +static struct hda_verb alc861_asus_ch6_init[] = { + /* set pin widget 1Ah (line in) for output (Back Surround)*/ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + /* set pin widget 18h (mic1) for output (CLFE)*/ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif + { } /* end */ +}; + +static struct hda_channel_mode alc861_asus_modes[2] = { + { 2, alc861_asus_ch2_init }, + { 6, alc861_asus_ch6_init }, +}; + /* patch-ALC861 */ static struct snd_kcontrol_new alc861_base_mixer[] = { @@ -6794,6 +6833,49 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { }, { } /* end */ }; + +static struct snd_kcontrol_new alc861_asus_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), + + /* Input mixer control */ + HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), /* was HDA_INPUT (why?) */ + + /* Capture mixer control */ + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + .private_value = ARRAY_SIZE(alc861_asus_modes), + }, + { } +}; + /* * generic initialization of ADC, input mixers and output mixers @@ -6983,6 +7065,68 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = { { } }; +static struct hda_verb alc861_asus_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) | according to codec#0 this is the HP jack*/ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ + /* route front PCM to HP */ + { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* this has to be set to VREF80 */ + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, 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(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + + /* * generic initialization of ADC, input mixers and output mixers */ @@ -7354,10 +7498,13 @@ static struct hda_board_config alc861_cfg_tbl[] = { { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072, .config = ALC861_UNIWILL_M31 }, { .modelname = "toshiba", .config = ALC861_TOSHIBA }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1338, - .config = ALC861_TOSHIBA }, { .pci_subvendor = 0x1179, .pci_subdevice = 0xff10, .config = ALC861_TOSHIBA }, + { .modelname = "asus", .config = ALC861_ASUS}, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1393, + .config = ALC861_ASUS }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1338, + .config = ALC861_ASUS }, { .modelname = "auto", .config = ALC861_AUTO }, {} }; @@ -7438,6 +7585,20 @@ static struct alc_config_preset alc861_presets[] = { .unsol_event = alc861_toshiba_unsol_event, .init_hook = alc861_toshiba_automute, }, + [ALC861_ASUS] = { + .mixers = { alc861_asus_mixer }, + .init_verbs = { alc861_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), + .channel_mode = alc861_asus_modes, + .need_dac_fix = 1, + .hp_nid = 0x06, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, }; -- cgit v1.2.3-59-g8ed1b From f1a63a38d2a885cc7e38c67b699171a7c5666d88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Oct 2006 18:25:29 +0200 Subject: [ALSA] ac97 - Suppress power-saving mode on non-supporting drivers Don't enable power-saving mode on drivers that don't support it. The supporting drivers set AC97_SCAP_POWER_SAVE to scaps at creation of ac97 instance. Currently enable on the following drivers: intel8x0, intel8x0m, atiixp, atiixp-modem, via82xx and via82xx-modem. Also, a bit clean up of power-saving stuff: - Don't create an own workq - Remove superfluous ifdefs Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ac97_codec.h | 2 +- sound/pci/ac97/ac97_codec.c | 41 +++++++++++++++++++++-------------------- sound/pci/atiixp.c | 2 +- sound/pci/atiixp_modem.c | 2 +- sound/pci/intel8x0.c | 2 +- sound/pci/intel8x0m.c | 2 +- sound/pci/via82xx.c | 2 +- sound/pci/via82xx_modem.c | 2 +- 8 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 5f7c78d9e379..5d3f0d8c0e61 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -375,6 +375,7 @@ #define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */ #define AC97_SCAP_NO_SPDIF (1<<9) /* don't build SPDIF controls */ #define AC97_SCAP_EAPD_LED (1<<10) /* EAPD as mute LED */ +#define AC97_SCAP_POWER_SAVE (1<<11) /* capable for aggresive power-saving */ /* ac97->flags */ #define AC97_HAS_PC_BEEP (1<<0) /* force PC Speaker usage */ @@ -511,7 +512,6 @@ struct snd_ac97 { #ifdef CONFIG_SND_AC97_POWER_SAVE unsigned int power_up; /* power states */ - struct workqueue_struct *power_workq; struct delayed_work power_work; #endif struct device dev; diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index d2994cb4c8c9..9da4977c0a0c 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -194,6 +194,13 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { static void update_power_regs(struct snd_ac97 *ac97); +#ifdef CONFIG_SND_AC97_POWER_SAVE +#define ac97_is_power_save_mode(ac97) \ + ((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save) +#else +#define ac97_is_power_save_mode(ac97) 0 +#endif + /* * I/O routines @@ -982,8 +989,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97) { if (ac97) { #ifdef CONFIG_SND_AC97_POWER_SAVE - if (ac97->power_workq) - destroy_workqueue(ac97->power_workq); + cancel_delayed_work(&ac97->power_work); #endif snd_ac97_proc_done(ac97); if (ac97->bus) @@ -1989,7 +1995,6 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, mutex_init(&ac97->reg_mutex); mutex_init(&ac97->page_mutex); #ifdef CONFIG_SND_AC97_POWER_SAVE - ac97->power_workq = create_workqueue("ac97"); INIT_DELAYED_WORK(&ac97->power_work, do_update_power); #endif @@ -2275,15 +2280,13 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97) udelay(100); power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */ snd_ac97_write(ac97, AC97_POWERDOWN, power); -#ifdef CONFIG_SND_AC97_POWER_SAVE - if (power_save) { + if (ac97_is_power_save_mode(ac97)) { udelay(100); /* AC-link powerdown, internal Clk disable */ /* FIXME: this may cause click noises on some boards */ power |= AC97_PD_PR4 | AC97_PD_PR5; snd_ac97_write(ac97, AC97_POWERDOWN, power); } -#endif } @@ -2337,14 +2340,16 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup) } } - if (power_save && !powerup && ac97->power_workq) + if (ac97_is_power_save_mode(ac97) && !powerup) /* adjust power-down bits after two seconds delay * (for avoiding loud click noises for many (OSS) apps * that open/close frequently) */ - queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2); - else + schedule_delayed_work(&ac97->power_work, HZ*2); + else { + cancel_delayed_work(&ac97->power_work); update_power_regs(ac97); + } return 0; } @@ -2357,19 +2362,15 @@ static void update_power_regs(struct snd_ac97 *ac97) unsigned int power_up, bits; int i; + power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC); + power_up |= (1 << PWIDX_MIC); + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) + power_up |= (1 << PWIDX_SURR); + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) + power_up |= (1 << PWIDX_CLFE); #ifdef CONFIG_SND_AC97_POWER_SAVE - if (power_save) + if (ac97_is_power_save_mode(ac97)) power_up = ac97->power_up; - else { -#endif - power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC); - power_up |= (1 << PWIDX_MIC); - if (ac97->scaps & AC97_SCAP_SURROUND_DAC) - power_up |= (1 << PWIDX_SURR); - if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) - power_up |= (1 << PWIDX_CLFE); -#ifdef CONFIG_SND_AC97_POWER_SAVE - } #endif if (power_up) { if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) { diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 476c3433073e..86710df71a8e 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1396,7 +1396,7 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock, ac97.private_data = chip; ac97.pci = chip->pci; ac97.num = i; - ac97.scaps = AC97_SCAP_SKIP_MODEM; + ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; if (! chip->spdif_over_aclink) ac97.scaps |= AC97_SCAP_NO_SPDIF; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index cc2e6b9d407e..904023fe4f26 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1090,7 +1090,7 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) ac97.private_data = chip; ac97.pci = chip->pci; ac97.num = i; - ac97.scaps = AC97_SCAP_SKIP_AUDIO; + ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { chip->ac97[i] = NULL; /* to be sure */ snd_printdd("atiixp-modem: codec %d not available for modem\n", i); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 28d5d9deb892..f8aef131be7a 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2057,7 +2057,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_intel8x0_mixer_free_ac97; - ac97.scaps = AC97_SCAP_SKIP_MODEM; + ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; if (chip->xbox) ac97.scaps |= AC97_SCAP_DETECT_BY_VENDOR; if (chip->device_type != DEVICE_ALI) { diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 936c3cf16936..c155e1f3a0e5 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -830,7 +830,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock) memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_intel8x0_mixer_free_ac97; - ac97.scaps = AC97_SCAP_SKIP_AUDIO; + ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE; glob_sta = igetdword(chip, ICHREG(GLOB_STA)); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a572b018807f..0440df7de37d 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1823,7 +1823,7 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *qui ac97.private_data = chip; ac97.private_free = snd_via82xx_mixer_free_ac97; ac97.pci = chip->pci; - ac97.scaps = AC97_SCAP_SKIP_MODEM; + ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) return err; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 17d6b847585f..b338e15db0d9 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -900,7 +900,7 @@ static int __devinit snd_via82xx_mixer_new(struct via82xx_modem *chip) ac97.private_data = chip; ac97.private_free = snd_via82xx_mixer_free_ac97; ac97.pci = chip->pci; - ac97.scaps = AC97_SCAP_SKIP_AUDIO; + ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE; ac97.num = chip->ac97_secondary; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) -- cgit v1.2.3-59-g8ed1b From 06bf2f495aabfdbe9d5db7b910fa75dd7f72131a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Oct 2006 19:49:39 +0200 Subject: [ALSA] hda-codec - Fix model for Lenovo A60 desktop Add a proper model entry (3stack) for Lenovo A60 desktop with AD1986a codec to fix noise problems. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_analog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 076365bc10e9..5e63123f70b8 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -800,6 +800,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb, .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */ + { .pci_subvendor = 0x17aa, .pci_subdevice = 0x1017, + .config = AD1986A_3STACK }, /* Lenovo A60 desktop */ { .modelname = "laptop", .config = AD1986A_LAPTOP }, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, .config = AD1986A_LAPTOP }, /* FSC V2060 */ -- cgit v1.2.3-59-g8ed1b From 8b65727bf07abc0b3fdac4fcf2f90c5882d65f4f Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 26 Oct 2006 17:12:59 +0200 Subject: [ALSA] hda: add dig mic support for sigmatel codecs Adds support for digital microphone pin widgets on SigmaTel codecs. Enables support only on the 9205 codecs for now. Signed-off-by: Matt Porter Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_sigmatel.c | 130 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fe51ef3e49d2..8f52372d66a2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -44,7 +44,7 @@ #define STAC_922X_MODELS 4 /* number of 922x models */ #define STAC_D965_3ST 4 #define STAC_D965_5ST 5 -#define STAC_927X_MODELS 6 /* number of 922x models */ +#define STAC_927X_MODELS 6 /* number of 927x models */ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; @@ -67,6 +67,9 @@ struct sigmatel_spec { unsigned int num_adcs; hda_nid_t *mux_nids; unsigned int num_muxes; + hda_nid_t *dmic_nids; + unsigned int num_dmics; + hda_nid_t dmux_nid; hda_nid_t dig_in_nid; /* pin widgets */ @@ -80,6 +83,8 @@ struct sigmatel_spec { struct snd_kcontrol_new *mixer; /* capture source */ + struct hda_input_mux *dinput_mux; + unsigned int cur_dmux; struct hda_input_mux *input_mux; unsigned int cur_mux[3]; @@ -92,6 +97,7 @@ struct sigmatel_spec { struct auto_pin_cfg autocfg; unsigned int num_kctl_alloc, num_kctl_used; struct snd_kcontrol_new *kctl_alloc; + struct hda_input_mux private_dimux; struct hda_input_mux private_imux; }; @@ -131,6 +137,10 @@ static hda_nid_t stac9205_mux_nids[2] = { 0x19, 0x1a }; +static hda_nid_t stac9205_dmic_nids[3] = { + 0x17, 0x18, 0 +}; + static hda_nid_t stac9200_pin_nids[8] = { 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, @@ -154,6 +164,34 @@ static hda_nid_t stac9205_pin_nids[12] = { }; +static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->dinput_mux, uinfo); +} + +static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_dmux; + return 0; +} + +static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, + spec->dmux_nid, &spec->cur_dmux); +} + static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -279,6 +317,14 @@ static snd_kcontrol_new_t stac927x_mixer[] = { }; static snd_kcontrol_new_t stac9205_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", + .count = 1, + .info = stac92xx_dmux_enum_info, + .get = stac92xx_dmux_enum_get, + .put = stac92xx_dmux_enum_put, + }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -585,8 +631,8 @@ static struct hda_board_config stac927x_cfg_tbl[] = { static unsigned int ref9205_pin_configs[12] = { 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x40000100, 0x40000100, - 0x40000100, 0x40000100, 0x01441030, 0x01c41030 + 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; static unsigned int *stac9205_brd_tbl[] = { @@ -1154,6 +1200,58 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return 0; } +/* labels for dmic mux inputs */ +const char *stac92xx_dmic_labels[5] = { + "Analog Inputs", "Digital Mic 1", "Digital Mic 2", + "Digital Mic 3", "Digital Mic 4" +}; + +/* create playback/capture controls for input pins on dmic capable codecs */ +static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *dimux = &spec->private_dimux; + hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; + int i, j; + + dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; + dimux->items[dimux->num_items].index = 0; + dimux->num_items++; + + for (i = 0; i < spec->num_dmics; i++) { + int index; + int num_cons; + unsigned int def_conf; + + def_conf = snd_hda_codec_read(codec, + spec->dmic_nids[i], + 0, + AC_VERB_GET_CONFIG_DEFAULT, + 0); + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + + num_cons = snd_hda_get_connections(codec, + spec->dmux_nid, + con_lst, + HDA_MAX_NUM_INPUTS); + for (j = 0; j < num_cons; j++) + if (con_lst[j] == spec->dmic_nids[i]) { + index = j; + goto found; + } + continue; +found: + dimux->items[dimux->num_items].label = + stac92xx_dmic_labels[dimux->num_items]; + dimux->items[dimux->num_items].index = index; + dimux->num_items++; + } + + return 0; +} + /* create playback/capture controls for input pins */ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { @@ -1238,7 +1336,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out struct sigmatel_spec *spec = codec->spec; int err; - if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) + if ((err = snd_hda_parse_pin_def_config(codec, + &spec->autocfg, + spec->dmic_nids)) < 0) return err; if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ @@ -1254,6 +1354,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out (err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) return err; + if (spec->num_dmics > 0) + if ((err = stac92xx_auto_create_dmic_input_ctls(codec, + &spec->autocfg)) < 0) + return err; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; if (spec->multiout.max_channels > 2) spec->surr_switch = 1; @@ -1267,6 +1372,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->input_mux = &spec->private_imux; + spec->dinput_mux = &spec->private_dimux; return 1; } @@ -1366,6 +1472,7 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->input_mux = &spec->private_imux; + spec->dinput_mux = &spec->private_dimux; return 1; } @@ -1448,6 +1555,11 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, nid, pinctl); } } + if (spec->num_dmics > 0) + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); + if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, AC_PINCTL_OUT_EN); @@ -1618,6 +1730,7 @@ static int patch_stac9200(struct hda_codec *codec) spec->adc_nids = stac9200_adc_nids; spec->mux_nids = stac9200_mux_nids; spec->num_muxes = 1; + spec->num_dmics = 0; spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; @@ -1663,6 +1776,7 @@ static int patch_stac922x(struct hda_codec *codec) spec->adc_nids = stac922x_adc_nids; spec->mux_nids = stac922x_mux_nids; spec->num_muxes = 2; + spec->num_dmics = 0; spec->init = stac922x_core_init; spec->mixer = stac922x_mixer; @@ -1714,6 +1828,7 @@ static int patch_stac927x(struct hda_codec *codec) spec->adc_nids = stac927x_adc_nids; spec->mux_nids = stac927x_mux_nids; spec->num_muxes = 3; + spec->num_dmics = 0; spec->init = d965_core_init; spec->mixer = stac9227_mixer; break; @@ -1721,6 +1836,7 @@ static int patch_stac927x(struct hda_codec *codec) spec->adc_nids = stac927x_adc_nids; spec->mux_nids = stac927x_mux_nids; spec->num_muxes = 3; + spec->num_dmics = 0; spec->init = d965_core_init; spec->mixer = stac9227_mixer; break; @@ -1728,6 +1844,7 @@ static int patch_stac927x(struct hda_codec *codec) spec->adc_nids = stac927x_adc_nids; spec->mux_nids = stac927x_mux_nids; spec->num_muxes = 3; + spec->num_dmics = 0; spec->init = stac927x_core_init; spec->mixer = stac927x_mixer; } @@ -1773,7 +1890,10 @@ static int patch_stac9205(struct hda_codec *codec) spec->adc_nids = stac9205_adc_nids; spec->mux_nids = stac9205_mux_nids; - spec->num_muxes = 3; + spec->num_muxes = 2; + spec->dmic_nids = stac9205_dmic_nids; + spec->num_dmics = 2; + spec->dmux_nid = 0x1d; spec->init = stac9205_core_init; spec->mixer = stac9205_mixer; -- cgit v1.2.3-59-g8ed1b From 219e281f4627a395aaceff0e4a257cd18608e145 Mon Sep 17 00:00:00 2001 From: Hubert Kahlert Date: Tue, 31 Oct 2006 15:31:27 +0100 Subject: [ALSA] Fix mask to stop AT91 SSC clock on shutdown This patch by Frank Mandarino and Hubert Kahlert fixes a bug in the AT91 SSC (i2s) shutdown code that would erroneously disable other AT91 peripheral clocks. Signed-off-by: Hubert Kahlert Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91rm9200-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c index 2eee427b1e5c..e3e6345fc8be 100644 --- a/sound/soc/at91/at91rm9200-i2s.c +++ b/sound/soc/at91/at91rm9200-i2s.c @@ -373,7 +373,7 @@ static void at91rm9200_i2s_shutdown(struct snd_pcm_substream *substream) if (!ssc_p->dir_mask) { /* Shutdown the SSC clock. */ DBG("Stopping pid %d clock\n", ssc_p->pid); - at91_sys_write(AT91_PMC_PCDR, ssc_p->pid); + at91_sys_write(AT91_PMC_PCDR, 1<pid); if (ssc_p->initialized) free_irq(ssc_p->pid, ssc_p); -- cgit v1.2.3-59-g8ed1b From de66d53e46f39de6ea3261609fdb92900bb34a42 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 6 Nov 2006 09:18:34 +0100 Subject: [ALSA] sb16: add request_firmware() Load the CSP programs using request_firmware(), if possible, instead of using the built-in firmware blobs. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- include/sound/sb16_csp.h | 14 +++++++++++ sound/isa/Kconfig | 1 + sound/isa/sb/sb16_csp.c | 61 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/include/sound/sb16_csp.h b/include/sound/sb16_csp.h index caf6fe21514d..736eac71d053 100644 --- a/include/sound/sb16_csp.h +++ b/include/sound/sb16_csp.h @@ -114,9 +114,21 @@ struct snd_sb_csp_info { #ifdef __KERNEL__ #include "sb.h" #include "hwdep.h" +#include struct snd_sb_csp; +/* indices for the known CSP programs */ +enum { + CSP_PROGRAM_MULAW, + CSP_PROGRAM_ALAW, + CSP_PROGRAM_ADPCM_INIT, + CSP_PROGRAM_ADPCM_PLAYBACK, + CSP_PROGRAM_ADPCM_CAPTURE, + + CSP_PROGRAM_COUNT +}; + /* * CSP operators */ @@ -159,6 +171,8 @@ struct snd_sb_csp { struct snd_kcontrol *qsound_space; struct mutex access_mutex; /* locking */ + + const struct firmware *csp_programs[CSP_PROGRAM_COUNT]; }; int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 57371f1a441f..565ed2add38b 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -358,6 +358,7 @@ config SND_SBAWE config SND_SB16_CSP bool "Sound Blaster 16/AWE CSP support" depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC) + select FW_LOADER help Say Y here to include support for the CSP core. This special coprocessor can do variable tasks like various compression and diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index fcd638090a9e..3d9d7e0107ca 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -161,10 +161,13 @@ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep) */ static void snd_sb_csp_free(struct snd_hwdep *hwdep) { + int i; struct snd_sb_csp *p = hwdep->private_data; if (p) { if (p->running & SNDRV_SB_CSP_ST_RUNNING) snd_sb_csp_stop(p); + for (i = 0; i < ARRAY_SIZE(p->csp_programs); ++i) + release_firmware(p->csp_programs[i]); kfree(p); } } @@ -687,8 +690,50 @@ static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __use return err; } +#define FIRMWARE_IN_THE_KERNEL + +#ifdef FIRMWARE_IN_THE_KERNEL #include "sb16_csp_codecs.h" +static const struct firmware snd_sb_csp_static_programs[] = { + { .data = mulaw_main, .size = sizeof mulaw_main }, + { .data = alaw_main, .size = sizeof alaw_main }, + { .data = ima_adpcm_init, .size = sizeof ima_adpcm_init }, + { .data = ima_adpcm_playback, .size = sizeof ima_adpcm_playback }, + { .data = ima_adpcm_capture, .size = sizeof ima_adpcm_capture }, +}; +#endif + +static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags) +{ + static const char *const names[] = { + "sb16/mulaw_main.csp", + "sb16/alaw_main.csp", + "sb16/ima_adpcm_init.csp", + "sb16/ima_adpcm_playback.csp", + "sb16/ima_adpcm_capture.csp", + }; + const struct firmware *program; + int err; + + BUILD_BUG_ON(ARRAY_SIZE(names) != CSP_PROGRAM_COUNT); + program = p->csp_programs[index]; + if (!program) { + err = request_firmware(&program, names[index], + p->chip->card->dev); + if (err >= 0) + p->csp_programs[index] = program; + else { +#ifdef FIRMWARE_IN_THE_KERNEL + program = &snd_sb_csp_static_programs[index]; +#else + return err; +#endif + } + } + return snd_sb_csp_load(p, program->data, program->size, flags); +} + /* * autoload hardware codec if necessary * return 0 if CSP is loaded and ready to run (p->running != 0) @@ -708,27 +753,27 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec } else { switch (pcm_sfmt) { case SNDRV_PCM_FORMAT_MU_LAW: - err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0); + err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_MULAW, 0); p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; break; case SNDRV_PCM_FORMAT_A_LAW: - err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0); + err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ALAW, 0); p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; break; case SNDRV_PCM_FORMAT_IMA_ADPCM: - err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init), - SNDRV_SB_CSP_LOAD_INITBLOCK); + err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ADPCM_INIT, + SNDRV_SB_CSP_LOAD_INITBLOCK); if (err) break; if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { - err = snd_sb_csp_load(p, &ima_adpcm_playback[0], - sizeof(ima_adpcm_playback), 0); + err = snd_sb_csp_firmware_load + (p, CSP_PROGRAM_ADPCM_PLAYBACK, 0); p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; } else { - err = snd_sb_csp_load(p, &ima_adpcm_capture[0], - sizeof(ima_adpcm_capture), 0); + err = snd_sb_csp_firmware_load + (p, CSP_PROGRAM_ADPCM_CAPTURE, 0); p->mode = SNDRV_SB_CSP_MODE_DSP_READ; } p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; -- cgit v1.2.3-59-g8ed1b From 59540fe85924ecb7b9760ab422cffaea0c3ce43a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 6 Nov 2006 09:20:04 +0100 Subject: [ALSA] wavefront: simplify YSS225 register initialization Instead of using a somewhat algorithmic approach of initializing the YSS225's registers, just use a simple series of port/value pairs. This makes it easier to later replace or entirely remove the register data blob. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- include/sound/snd_wavefront.h | 2 + sound/isa/wavefront/wavefront.c | 1 + sound/isa/wavefront/wavefront_fx.c | 789 +---------- sound/isa/wavefront/yss225.c | 2739 ++++++++++++++++++++++++++++++++++++ 4 files changed, 2763 insertions(+), 768 deletions(-) create mode 100644 sound/isa/wavefront/yss225.c diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index 0b9e5de94ff1..9688d4be918e 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -85,6 +85,7 @@ struct _snd_wavefront { char hw_version[2]; /* major = [0], minor = [1] */ char israw; /* needs Motorola microcode */ char has_fx; /* has FX processor (Tropez+) */ + char fx_initialized; /* FX's register pages initialized */ char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ @@ -94,6 +95,7 @@ struct _snd_wavefront { spinlock_t irq_lock; wait_queue_head_t interrupt_sleeper; snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ + struct snd_card *card; }; struct _snd_wavefront_card { diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 85db535aea9b..e2fdd5fd39d0 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -402,6 +402,7 @@ static struct snd_card *snd_wavefront_card_new(int dev) init_waitqueue_head(&acard->wavefront.interrupt_sleeper); spin_lock_init(&acard->wavefront.midi.open); spin_lock_init(&acard->wavefront.midi.virtual); + acard->wavefront.card = card; card->private_free = snd_wavefront_free; return card; diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index 4f0846feb73f..2e03dc504d4c 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -32,325 +32,9 @@ #define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ #define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ -/* weird stuff, derived from port I/O tracing with dosemu */ - -static unsigned char page_zero[] __devinitdata = { -0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, -0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, -0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, -0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, -0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, -0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, -0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, -0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, -0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, -0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, -0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, -0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, -0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, -0x1d, 0x02, 0xdf -}; - -static unsigned char page_one[] __devinitdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, -0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, -0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, -0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, -0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, -0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, -0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, -0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, -0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, -0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, -0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, -0x60, 0x00, 0x1b -}; - -static unsigned char page_two[] __devinitdata = { -0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, -0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, -0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, -0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, -0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, -0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, -0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, -0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 -}; - -static unsigned char page_three[] __devinitdata = { -0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, -0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, -0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, -0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, -0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 -}; - -static unsigned char page_four[] __devinitdata = { -0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, -0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, -0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, -0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, -0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 -}; - -static unsigned char page_six[] __devinitdata = { -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, -0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, -0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, -0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, -0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, -0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, -0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, -0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, -0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, -0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, -0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, -0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, -0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, -0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, -0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, -0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, -0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, -0x80, 0x00, 0x7e, 0x80, 0x80 -}; - -static unsigned char page_seven[] __devinitdata = { -0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, -0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, -0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, -0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, -0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, -0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, -0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, -0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, -0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, -0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, -0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, -0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00 -}; - -static unsigned char page_zero_v2[] __devinitdata = { -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char page_one_v2[] __devinitdata = { -0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char page_two_v2[] __devinitdata = { -0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -static unsigned char page_three_v2[] __devinitdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -static unsigned char page_four_v2[] __devinitdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char page_seven_v2[] __devinitdata = { -0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char mod_v2[] __devinitdata = { -0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, -0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, -0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, -0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, -0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, -0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, -0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, -0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, -0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, -0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, -0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, -0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, -0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, -0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, -0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, -0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, -0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, -0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, -0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, -0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, -0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, -0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, -0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, -0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, -0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, -0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, -0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, -0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 -}; -static unsigned char coefficients[] __devinitdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, -0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, -0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, -0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, -0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, -0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, -0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, -0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, -0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, -0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, -0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, -0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, -0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, -0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, -0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, -0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, -0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, -0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, -0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, -0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, -0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, -0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, -0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, -0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, -0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, -0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, -0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, -0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, -0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, -0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, -0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, -0xba -}; -static unsigned char coefficients2[] __devinitdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, -0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, -0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, -0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, -0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 -}; -static unsigned char coefficients3[] __devinitdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, -0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, -0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, -0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, -0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, -0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, -0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, -0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, -0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, -0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, -0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, -0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, -0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, -0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, -0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, -0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, -0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, -0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, -0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, -0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, -0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, -0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, -0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, -0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, -0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, -0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, -0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, -0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, -0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, -0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, -0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, -0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, -0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, -0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, -0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, -0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, -0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff -}; +#define WAIT_IDLE 0xff + +#include "yss225.c" static int wavefront_fx_idle (snd_wavefront_t *dev) @@ -555,465 +239,34 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file, of the port I/O done, using the Yamaha faxback document as a guide to add more logic to the code. Its really pretty weird. - There was an alternative approach of just dumping the whole I/O + This is the approach of just dumping the whole I/O sequence as a series of port/value pairs and a simple loop - that output it. However, I hope that eventually I'll get more - control over what this code does, and so I tried to stick with - a somewhat "algorithmic" approach. + that outputs it. */ - int __devinit snd_wavefront_fx_start (snd_wavefront_t *dev) - { - unsigned int i, j; + unsigned int i; - /* Set all bits for all channels on the MOD unit to zero */ - /* XXX But why do this twice ? */ - - for (j = 0; j < 2; j++) { - for (i = 0x10; i <= 0xff; i++) { - - if (!wavefront_fx_idle (dev)) { - return (-1); - } - - outb (i, dev->fx_mod_addr); - outb (0x0, dev->fx_mod_data); - } - } + if (dev->fx_initialized) + return 0; - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x02, dev->fx_op); /* mute on */ - - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x44, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x42, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x43, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x7c, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x7e, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x46, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x49, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x47, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x4a, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - - /* either because of stupidity by TB's programmers, or because it - actually does something, rezero the MOD page. - */ - for (i = 0x10; i <= 0xff; i++) { - - if (!wavefront_fx_idle (dev)) { - return (-1); + for (i = 0; i < ARRAY_SIZE(yss225_registers); ++i) { + if (yss225_registers[i].addr >= 8 && + yss225_registers[i].addr < 16) { + outb(yss225_registers[i].data, + yss225_registers[i].addr + dev->base); + } else if (yss225_registers[i].addr == WAIT_IDLE) { + if (!wavefront_fx_idle(dev)) + return -1; + } else { + snd_printk(KERN_ERR "invalid address" + " in register data\n"); + return -1; } - - outb (i, dev->fx_mod_addr); - outb (0x0, dev->fx_mod_data); - } - /* load page zero */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x00, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero); i += 2) { - outb (page_zero[i], dev->fx_dsp_msb); - outb (page_zero[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - /* Now load page one */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x01, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_one); i += 2) { - outb (page_one[i], dev->fx_dsp_msb); - outb (page_one[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x02, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_two); i++) { - outb (page_two[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x03, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_three); i++) { - outb (page_three[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x04, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_four); i++) { - outb (page_four[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); } - /* Load memory area (page six) */ - - outb (FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x06, dev->fx_dsp_page); - - for (i = 0; i < sizeof (page_six); i += 3) { - outb (page_six[i], dev->fx_dsp_addr); - outb (page_six[i+1], dev->fx_dsp_msb); - outb (page_six[i+2], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x07, dev->fx_dsp_page); - outb (0x00, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven); i += 2) { - outb (page_seven[i], dev->fx_dsp_msb); - outb (page_seven[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - /* Now setup the MOD area. We do this algorithmically in order to - save a little data space. It could be done in the same fashion - as the "pages". - */ - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev->fx_mod_addr); - outb (i, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x02, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0xb0; i <= 0xbf; i++) { - outb (i, dev->fx_mod_addr); - outb (0x20, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0xf0; i <= 0xff; i++) { - outb (i, dev->fx_mod_addr); - outb (0x20, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0x10; i <= 0x1d; i++) { - outb (i, dev->fx_mod_addr); - outb (0xff, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x1e, dev->fx_mod_addr); - outb (0x40, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - for (i = 0x1f; i <= 0x2d; i++) { - outb (i, dev->fx_mod_addr); - outb (0xff, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x2e, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - for (i = 0x2f; i <= 0x3e; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x3f, dev->fx_mod_addr); - outb (0x20, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - for (i = 0x40; i <= 0x4d; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x4e, dev->fx_mod_addr); - outb (0x0e, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x4f, dev->fx_mod_addr); - outb (0x0e, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - - for (i = 0x50; i <= 0x6b; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x6c, dev->fx_mod_addr); - outb (0x40, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - outb (0x6d, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - outb (0x6e, dev->fx_mod_addr); - outb (0x40, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - outb (0x6f, dev->fx_mod_addr); - outb (0x40, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - for (i = 0x70; i <= 0x7f; i++) { - outb (i, dev->fx_mod_addr); - outb (0xc0, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0x80; i <= 0xaf; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0xc0; i <= 0xdd; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0xde, dev->fx_mod_addr); - outb (0x10, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0xdf, dev->fx_mod_addr); - outb (0x10, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - - for (i = 0xe0; i <= 0xef; i++) { - outb (i, dev->fx_mod_addr); - outb (0x00, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev->fx_mod_addr); - outb (i, dev->fx_mod_data); - outb (0x02, dev->fx_mod_addr); - outb (0x01, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (0x02, dev->fx_op); /* mute on */ - - /* Now set the coefficients and so forth for the programs above */ - - for (i = 0; i < sizeof (coefficients); i += 4) { - outb (coefficients[i], dev->fx_dsp_page); - outb (coefficients[i+1], dev->fx_dsp_addr); - outb (coefficients[i+2], dev->fx_dsp_msb); - outb (coefficients[i+3], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - /* Some settings (?) that are too small to bundle into loops */ - - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x1e, dev->fx_mod_addr); - outb (0x14, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0xde, dev->fx_mod_addr); - outb (0x20, dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0xdf, dev->fx_mod_addr); - outb (0x20, dev->fx_mod_data); - - /* some more coefficients */ - - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x06, dev->fx_dsp_page); - outb (0x78, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x40, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x03, dev->fx_dsp_addr); - outb (0x0f, dev->fx_dsp_msb); - outb (0xff, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x0b, dev->fx_dsp_addr); - outb (0x0f, dev->fx_dsp_msb); - outb (0xff, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x02, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x0a, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x46, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - outb (0x07, dev->fx_dsp_page); - outb (0x49, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - - /* Now, for some strange reason, lets reload every page - and all the coefficients over again. I have *NO* idea - why this is done. I do know that no sound is produced - is this phase is omitted. - */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x00, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero_v2); i += 2) { - outb (page_zero_v2[i], dev->fx_dsp_msb); - outb (page_zero_v2[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x01, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_one_v2); i += 2) { - outb (page_one_v2[i], dev->fx_dsp_msb); - outb (page_one_v2[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - if (!wavefront_fx_idle (dev)) return (-1); - if (!wavefront_fx_idle (dev)) return (-1); - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x02, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_two_v2); i++) { - outb (page_two_v2[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x03, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_three_v2); i++) { - outb (page_three_v2[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x04, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_four_v2); i++) { - outb (page_four_v2[i], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x06, dev->fx_dsp_page); - - /* Page six v.2 is algorithmic */ - - for (i = 0x10; i <= 0x3e; i += 2) { - outb (i, dev->fx_dsp_addr); - outb (0x00, dev->fx_dsp_msb); - outb (0x00, dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); - outb (0x07, dev->fx_dsp_page); - outb (0x10, dev->fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven_v2); i += 2) { - outb (page_seven_v2[i], dev->fx_dsp_msb); - outb (page_seven_v2[i+1], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0x00; i < sizeof(mod_v2); i += 2) { - outb (mod_v2[i], dev->fx_mod_addr); - outb (mod_v2[i+1], dev->fx_mod_data); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0; i < sizeof (coefficients2); i += 4) { - outb (coefficients2[i], dev->fx_dsp_page); - outb (coefficients2[i+1], dev->fx_dsp_addr); - outb (coefficients2[i+2], dev->fx_dsp_msb); - outb (coefficients2[i+3], dev->fx_dsp_lsb); - if (!wavefront_fx_idle (dev)) return (-1); - } - - for (i = 0; i < sizeof (coefficients3); i += 2) { - int x; - - outb (0x07, dev->fx_dsp_page); - x = (i % 4) ? 0x4e : 0x4c; - outb (x, dev->fx_dsp_addr); - outb (coefficients3[i], dev->fx_dsp_msb); - outb (coefficients3[i+1], dev->fx_dsp_lsb); - } - - outb (0x00, dev->fx_op); /* mute off */ - if (!wavefront_fx_idle (dev)) return (-1); - + dev->fx_initialized = 1; return (0); } diff --git a/sound/isa/wavefront/yss225.c b/sound/isa/wavefront/yss225.c new file mode 100644 index 000000000000..9f6be3ff8ecf --- /dev/null +++ b/sound/isa/wavefront/yss225.c @@ -0,0 +1,2739 @@ +/* + * Copyright (c) 1998-2002 by Paul Davis + * + * 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 + */ + +/* weird stuff, derived from port I/O tracing with dosemu */ + +static const struct { + unsigned char addr; + unsigned char data; +} yss225_registers[] __devinitdata = { +/* Set all bits for all channels on the MOD unit to zero */ +{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 }, + +/* XXX But why do this twice? */ +{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 }, + +/* mute on */ +{ WAIT_IDLE }, { 0x8, 0x02 }, + +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, + +/* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. */ +{ WAIT_IDLE }, { 0xe, 0x10 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x11 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x12 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x13 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x14 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x15 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x16 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x17 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x18 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x19 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x1f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x20 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x21 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x22 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x23 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x24 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x25 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x26 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x27 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x28 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x29 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x2f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x30 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x31 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x32 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x33 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x34 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x35 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x36 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x37 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x38 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x39 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x3f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x40 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x41 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x42 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x43 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x44 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x45 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x46 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x47 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x48 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x49 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x4f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x50 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x51 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x52 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x53 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x54 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x55 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x56 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x57 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x58 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x59 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x5f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x60 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x61 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x62 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x63 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x64 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x65 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x66 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x67 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x68 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x69 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x6f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x70 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x71 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x72 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x73 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x74 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x75 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x76 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x77 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x78 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x79 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x7f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x80 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x81 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x82 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x83 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x84 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x85 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x86 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x87 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x88 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x89 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x8f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x90 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x91 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x92 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x93 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x94 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x95 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x96 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x97 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x98 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x99 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9a }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9b }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9c }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9d }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9e }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0x9f }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xa9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xab }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xac }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xad }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xae }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xaf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xb9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xba }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xbf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xc9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xca }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xce }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xcf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xd9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xda }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xe9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xea }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xeb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xec }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xed }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xee }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xef }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf0 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf1 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf2 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf3 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf4 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf5 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf6 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf7 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf8 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xf9 }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfa }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfb }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfc }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfd }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xfe }, { 0xf, 0x00 }, +{ WAIT_IDLE }, { 0xe, 0xff }, { 0xf, 0x00 }, + +/* load page zero */ +{ 0x9, 0x05 }, { 0xb, 0x00 }, { 0xa, 0x00 }, + +{ 0xd, 0x01 }, { 0xc, 0x7c }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1e }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xf5 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x11 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x32 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x13 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x14 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x76 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x18 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x1a }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x17 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x10 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0xa0 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xd1 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0xf2 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x13 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xf4 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xe0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x15 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x16 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x17 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x50 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x71 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x92 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xb3 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xa0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xd4 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xf5 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x70 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0xa0 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x11 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x16 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x10 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x17 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1d }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xdf }, { WAIT_IDLE }, + +/* Now load page one */ +{ 0x9, 0x05 }, { 0xb, 0x01 }, { 0xa, 0x00 }, + +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1f }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xd8 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x19 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x18 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0xfa }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1a }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xa0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xd7 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xf7 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1c }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x3c }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x3f }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xdf }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x5d }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x7d }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0x9e }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xbe }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x03 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xdb }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xdb }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xe0 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xfb }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1b }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x02 }, { 0xa, 0x00 }, + +{ 0xc, 0xc4 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x25 }, { WAIT_IDLE }, +{ 0xc, 0x01 }, { WAIT_IDLE }, +{ 0xc, 0x06 }, { WAIT_IDLE }, +{ 0xc, 0xc4 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x25 }, { WAIT_IDLE }, +{ 0xc, 0x01 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x04 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x04 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x05 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x44 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x03 }, { 0xa, 0x00 }, + +{ 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x47 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x06 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x70 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x42 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x42 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x42 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x42 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x40 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x04 }, { 0xa, 0x00 }, + +{ 0xc, 0x63 }, { WAIT_IDLE }, +{ 0xc, 0x03 }, { WAIT_IDLE }, +{ 0xc, 0x26 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x2c }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x24 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x2e }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x01 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x22 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x22 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x22 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x62 }, { WAIT_IDLE }, +{ 0xc, 0x02 }, { WAIT_IDLE }, +{ 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xc, 0x01 }, { WAIT_IDLE }, +{ 0xc, 0x21 }, { WAIT_IDLE }, +{ 0xc, 0x01 }, { WAIT_IDLE }, + +/* Load memory area (page six) */ +{ 0x9, 0x01 }, { 0xb, 0x06 }, + +{ 0xa, 0x00 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x02 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x04 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x06 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x08 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x0a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x0c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x0e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x10 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x12 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x14 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x16 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x18 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x20 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x22 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x24 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x26 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x28 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x30 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x32 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x34 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x36 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x38 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x42 }, { 0xd, 0x03 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x44 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x46 }, { 0xd, 0x0a }, { 0xc, 0x21 }, { WAIT_IDLE }, +{ 0xa, 0x48 }, { 0xd, 0x0d }, { 0xc, 0x23 }, { WAIT_IDLE }, +{ 0xa, 0x4a }, { 0xd, 0x23 }, { 0xc, 0x1b }, { WAIT_IDLE }, +{ 0xa, 0x4c }, { 0xd, 0x37 }, { 0xc, 0x8f }, { WAIT_IDLE }, +{ 0xa, 0x4e }, { 0xd, 0x45 }, { 0xc, 0x77 }, { WAIT_IDLE }, +{ 0xa, 0x50 }, { 0xd, 0x52 }, { 0xc, 0xe2 }, { WAIT_IDLE }, +{ 0xa, 0x52 }, { 0xd, 0x1c }, { 0xc, 0x92 }, { WAIT_IDLE }, +{ 0xa, 0x54 }, { 0xd, 0x1c }, { 0xc, 0x52 }, { WAIT_IDLE }, +{ 0xa, 0x56 }, { 0xd, 0x07 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x58 }, { 0xd, 0x2f }, { 0xc, 0xc6 }, { WAIT_IDLE }, +{ 0xa, 0x5a }, { 0xd, 0x0b }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x5c }, { 0xd, 0x30 }, { 0xc, 0x06 }, { WAIT_IDLE }, +{ 0xa, 0x5e }, { 0xd, 0x17 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x60 }, { 0xd, 0x3d }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xa, 0x62 }, { 0xd, 0x29 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x64 }, { 0xd, 0x3e }, { 0xc, 0x41 }, { WAIT_IDLE }, +{ 0xa, 0x66 }, { 0xd, 0x39 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x68 }, { 0xd, 0x4c }, { 0xc, 0x48 }, { WAIT_IDLE }, +{ 0xa, 0x6a }, { 0xd, 0x49 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x6c }, { 0xd, 0x4c }, { 0xc, 0x6c }, { WAIT_IDLE }, +{ 0xa, 0x6e }, { 0xd, 0x11 }, { 0xc, 0xd2 }, { WAIT_IDLE }, +{ 0xa, 0x70 }, { 0xd, 0x16 }, { 0xc, 0x0c }, { WAIT_IDLE }, +{ 0xa, 0x72 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x74 }, { 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xa, 0x76 }, { 0xd, 0x0f }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x78 }, { 0xd, 0x00 }, { 0xc, 0x80 }, { WAIT_IDLE }, +{ 0xa, 0x7a }, { 0xd, 0x13 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x7c }, { 0xd, 0x80 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x7e }, { 0xd, 0x80 }, { 0xc, 0x80 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x07 }, { 0xa, 0x00 }, + +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x08 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0xe9 }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x8c }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x8c }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x1a }, { 0xc, 0x75 }, { WAIT_IDLE }, +{ 0xd, 0x0d }, { 0xc, 0x8b }, { WAIT_IDLE }, +{ 0xd, 0x04 }, { 0xc, 0xe9 }, { WAIT_IDLE }, +{ 0xd, 0x0b }, { 0xc, 0x16 }, { WAIT_IDLE }, +{ 0xd, 0x1a }, { 0xc, 0x38 }, { WAIT_IDLE }, +{ 0xd, 0x0d }, { 0xc, 0xc8 }, { WAIT_IDLE }, +{ 0xd, 0x04 }, { 0xc, 0x6f }, { WAIT_IDLE }, +{ 0xd, 0x0b }, { 0xc, 0x91 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x8f }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x62 }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x62 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x7b }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x97 }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0x97 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x52 }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0xf6 }, { WAIT_IDLE }, +{ 0xd, 0x06 }, { 0xc, 0xf6 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x19 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x55 }, { WAIT_IDLE }, +{ 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +/* Now setup the MOD area. */ +{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x08 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x09 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0a }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0b }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0c }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0d }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0e }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0f }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, + +{ 0xe, 0xb0 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb1 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb2 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb3 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb4 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb5 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb6 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb7 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb8 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb9 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xba }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xbb }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xbc }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xbd }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xbe }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xbf }, { 0xf, 0x20 }, { WAIT_IDLE }, + +{ 0xe, 0xf0 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf1 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf2 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf3 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf4 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf5 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf6 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf7 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf8 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf9 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xfa }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xfb }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xfc }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xfd }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xfe }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xff }, { 0xf, 0x20 }, { WAIT_IDLE }, + +{ 0xe, 0x10 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x11 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x12 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x13 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x14 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x15 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x16 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x17 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x18 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x19 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x1a }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x1b }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x1c }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x1d }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x1e }, { 0xf, 0x40 }, { WAIT_IDLE }, +{ 0xe, 0x1f }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x20 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x21 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x22 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x23 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x24 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x25 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x26 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x27 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x28 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x29 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x2a }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x2b }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x2c }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x2d }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x2e }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x2f }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x30 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x31 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x32 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x33 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x34 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x35 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x36 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x37 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x38 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x39 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3c }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3e }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x3f }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0x40 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x41 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x42 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x43 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x44 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x45 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x46 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x47 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x48 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x49 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x4a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x4b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x4c }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x4d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x4e }, { 0xf, 0x0e }, { WAIT_IDLE }, +{ 0xe, 0x4f }, { 0xf, 0x0e }, { WAIT_IDLE }, +{ 0xe, 0x50 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x51 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x52 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x53 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x54 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x55 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x56 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x57 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x58 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x59 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5c }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5e }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x5f }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x60 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x61 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x62 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x63 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x64 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x65 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x66 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x67 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x68 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x69 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x6a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x6b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x6c }, { 0xf, 0x40 }, { WAIT_IDLE }, +{ 0xe, 0x6d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x6e }, { 0xf, 0x40 }, { WAIT_IDLE }, +{ 0xe, 0x6f }, { 0xf, 0x40 }, { WAIT_IDLE }, +{ 0xe, 0x70 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x71 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x72 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x73 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x74 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x75 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x76 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x77 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x78 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x79 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7a }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7b }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7c }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7d }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7e }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x7f }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x80 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x81 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x82 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x83 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x84 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x85 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x86 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x87 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x88 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x89 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8c }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8e }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x8f }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x90 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x91 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x92 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x93 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x94 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x95 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x96 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x97 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x98 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x99 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9a }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9b }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9c }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9d }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9e }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x9f }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa8 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa9 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xaa }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xab }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xac }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xad }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xae }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xaf }, { 0xf, 0x00 }, { WAIT_IDLE }, + +{ 0xe, 0xc0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc8 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc9 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xca }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xcb }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xcc }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xcd }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xce }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xcf }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd8 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd9 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xda }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xdb }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xdc }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xdd }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xde }, { 0xf, 0x10 }, { WAIT_IDLE }, +{ 0xe, 0xdf }, { 0xf, 0x10 }, { WAIT_IDLE }, +{ 0xe, 0xe0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe8 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe9 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xea }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xeb }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xec }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xed }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xee }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xef }, { 0xf, 0x00 }, { WAIT_IDLE }, + +{ 0xe, 0x01 }, { 0xf, 0x00 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x01 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x02 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x03 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x04 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x05 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x06 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x07 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x08 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x09 }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0a }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0b }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0c }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0d }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0e }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x0f }, { 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, + +/* mute on */ +{ 0x8, 0x02 }, + +/* Now set the coefficients and so forth for the programs above */ +{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x4b }, { 0xd, 0x03 }, { 0xc, 0x11 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x4d }, { 0xd, 0x01 }, { 0xc, 0x32 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x01 }, { 0xa, 0x40 }, { 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xb, 0x01 }, { 0xa, 0x41 }, { 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x47 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x4a }, { 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x00 }, { 0xd, 0x01 }, { 0xc, 0x1c }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x44 }, { 0xd, 0x01 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x42 }, { 0xd, 0x01 }, { 0xc, 0x1a }, { WAIT_IDLE }, +{ 0xb, 0x00 }, { 0xa, 0x43 }, { 0xd, 0x01 }, { 0xc, 0x20 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x01 }, { 0xa, 0x40 }, { 0xd, 0x02 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xb, 0x01 }, { 0xa, 0x41 }, { 0xd, 0x02 }, { 0xc, 0x60 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x44 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x42 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x43 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x40 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x41 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x51 }, { 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x50 }, { 0xd, 0x06 }, { 0xc, 0x40 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4f }, { 0xd, 0x03 }, { 0xc, 0x81 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x53 }, { 0xd, 0x1a }, { 0xc, 0x76 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x54 }, { 0xd, 0x0d }, { 0xc, 0x8b }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x55 }, { 0xd, 0x04 }, { 0xc, 0xe9 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x56 }, { 0xd, 0x0b }, { 0xc, 0x17 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x57 }, { 0xd, 0x1a }, { 0xc, 0x38 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x58 }, { 0xd, 0x0d }, { 0xc, 0xc9 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x59 }, { 0xd, 0x04 }, { 0xc, 0x6f }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x5a }, { 0xd, 0x0b }, { 0xc, 0x91 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x73 }, { 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x74 }, { 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x75 }, { 0xd, 0x04 }, { 0xc, 0xd9 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x76 }, { 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x77 }, { 0xd, 0x14 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x78 }, { 0xd, 0x0d }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x79 }, { 0xd, 0x04 }, { 0xc, 0xd9 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7a }, { 0xd, 0x05 }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x5e }, { 0xd, 0x03 }, { 0xc, 0x68 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x5c }, { 0xd, 0x04 }, { 0xc, 0x31 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x5d }, { 0xd, 0x04 }, { 0xc, 0x31 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x62 }, { 0xd, 0x03 }, { 0xc, 0x52 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x60 }, { 0xd, 0x04 }, { 0xc, 0x76 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x61 }, { 0xd, 0x04 }, { 0xc, 0x76 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x66 }, { 0xd, 0x03 }, { 0xc, 0x2e }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x64 }, { 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x65 }, { 0xd, 0x04 }, { 0xc, 0xda }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x6a }, { 0xd, 0x02 }, { 0xc, 0xf6 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x68 }, { 0xd, 0x05 }, { 0xc, 0x62 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x69 }, { 0xd, 0x05 }, { 0xc, 0x62 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x46 }, { 0xd, 0x0a }, { 0xc, 0x22 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x48 }, { 0xd, 0x0d }, { 0xc, 0x24 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x6e }, { 0xd, 0x11 }, { 0xc, 0xd3 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x70 }, { 0xd, 0x15 }, { 0xc, 0xcb }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x52 }, { 0xd, 0x20 }, { 0xc, 0x93 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x54 }, { 0xd, 0x20 }, { 0xc, 0x54 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x4a }, { 0xd, 0x27 }, { 0xc, 0x1d }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x58 }, { 0xd, 0x2f }, { 0xc, 0xc8 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x5c }, { 0xd, 0x30 }, { 0xc, 0x07 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x4c }, { 0xd, 0x37 }, { 0xc, 0x90 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x60 }, { 0xd, 0x3d }, { 0xc, 0xdb }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x64 }, { 0xd, 0x3e }, { 0xc, 0x42 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x4e }, { 0xd, 0x45 }, { 0xc, 0x78 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x68 }, { 0xd, 0x4c }, { 0xc, 0x48 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x6c }, { 0xd, 0x4c }, { 0xc, 0x6c }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x50 }, { 0xd, 0x52 }, { 0xc, 0xe2 }, { WAIT_IDLE }, +{ 0xb, 0x06 }, { 0xa, 0x42 }, { 0xd, 0x02 }, { 0xc, 0xba }, { WAIT_IDLE }, + +/* Some settings (?) */ +{ WAIT_IDLE }, { 0xe, 0x1e }, { 0xf, 0x14 }, +{ WAIT_IDLE }, { 0xe, 0xde }, { 0xf, 0x20 }, +{ WAIT_IDLE }, { 0xe, 0xdf }, { 0xf, 0x20 }, + +/* some more coefficients */ +{ WAIT_IDLE }, { 0xb, 0x06 }, { 0xa, 0x78 }, { 0xd, 0x00 }, { 0xc, 0x40 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x03 }, { 0xd, 0x0f }, { 0xc, 0xff }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x0b }, { 0xd, 0x0f }, { 0xc, 0xff }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x02 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x0a }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ WAIT_IDLE }, { 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, + +/* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. */ +{ 0x9, 0x05 }, { 0xb, 0x00 }, { 0xa, 0x10 }, + +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x02 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x01 }, { 0xa, 0x10 }, + +{ 0xd, 0x01 }, { 0xc, 0xc0 }, { WAIT_IDLE }, +{ 0xd, 0x01 }, { 0xc, 0xfa }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x1a }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +{ WAIT_IDLE }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x02 }, { 0xa, 0x10 }, + +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x46 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x03 }, { 0xa, 0x10 }, + +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x04 }, { 0xa, 0x10 }, + +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xc, 0x00 }, { WAIT_IDLE }, + +/* Page six v.2 */ +{ 0x9, 0x01 }, { 0xb, 0x06 }, + +{ 0xa, 0x10 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x12 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x14 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x16 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x18 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x1e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x20 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x22 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x24 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x26 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x28 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x2e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x30 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x32 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x34 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x36 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x38 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xa, 0x3e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0x9, 0x05 }, { 0xb, 0x07 }, { 0xa, 0x10 }, + +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xb0 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb1 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb2 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb3 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb4 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb5 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb6 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xb7 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf0 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf1 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf2 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf3 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf4 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf5 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf6 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0xf7 }, { 0xf, 0x20 }, { WAIT_IDLE }, +{ 0xe, 0x10 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x11 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x12 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x13 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x14 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x15 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x16 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x17 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x20 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x21 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x22 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x23 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x24 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x25 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x26 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x27 }, { 0xf, 0xff }, { WAIT_IDLE }, +{ 0xe, 0x30 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x31 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x32 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x33 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x34 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x35 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x36 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x37 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x40 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x41 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x42 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x43 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x44 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x45 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x46 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x47 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x50 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x51 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x52 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x53 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x54 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x55 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x56 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x57 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x60 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x61 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x62 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x63 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x64 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x65 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x66 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x67 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x70 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x71 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x72 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x73 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x74 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x75 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x76 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x77 }, { 0xf, 0xc0 }, { WAIT_IDLE }, +{ 0xe, 0x80 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x81 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x82 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x83 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x84 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x85 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x86 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x87 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x90 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x91 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x92 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x93 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x94 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x95 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x96 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x97 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xa7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xc7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xd7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe0 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe1 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe2 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe3 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe4 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe5 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe6 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0xe7 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x00 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x02 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x03 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x04 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x05 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x06 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, +{ 0xe, 0x01 }, { 0xf, 0x07 }, { WAIT_IDLE }, +{ 0xe, 0x02 }, { 0xf, 0x01 }, { WAIT_IDLE }, + +{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x45 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x48 }, { 0xd, 0x0f }, { 0xc, 0xff }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7b }, { 0xd, 0x04 }, { 0xc, 0xcc }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7d }, { 0xd, 0x04 }, { 0xc, 0xcc }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x7e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x46 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x49 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x47 }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4a }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x00 }, { WAIT_IDLE }, + +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x00 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x00 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x01 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x01 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x02 }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x02 }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x03 }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x03 }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x04 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x04 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x05 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x05 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x06 }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x06 }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x07 }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x07 }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x08 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x08 }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x09 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x09 }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0a }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0a }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0b }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0b }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x00 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x28 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x51 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0x7a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xa3 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xcc }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0c }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0c }, { 0xc, 0xf5 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x1e }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x47 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x70 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0x99 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0xc2 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0d }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0d }, { 0xc, 0xeb }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x14 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x3d }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x66 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0x8f }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0xb8 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0e }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0e }, { 0xc, 0xe1 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x0a }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x33 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x5c }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0x85 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xae }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xd7 }, +{ 0xb, 0x07 }, { 0xa, 0x4c }, { 0xd, 0x0f }, { 0xc, 0xff }, +{ 0xb, 0x07 }, { 0xa, 0x4e }, { 0xd, 0x0f }, { 0xc, 0xff }, + +/* mute off */ +{ 0x8, 0x00 }, { WAIT_IDLE } +}; -- cgit v1.2.3-59-g8ed1b From 226968c7afd464b794f34f9ea8cb4bcfe48447dc Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 6 Nov 2006 09:21:58 +0100 Subject: [ALSA] wavefront: add request_firmware() Load the YSS225 register initialization data using request_firmware(), if possible, instead of using the built-in data blob. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/isa/Kconfig | 1 + sound/isa/wavefront/wavefront_fx.c | 51 ++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 565ed2add38b..4e3a9729f569 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -391,6 +391,7 @@ config SND_SSCAPE config SND_WAVEFRONT tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)" depends on SND + select FW_LOADER select SND_OPL3_LIB select SND_MPU401_UART select SND_CS4231_LIB diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index 2e03dc504d4c..15331ed88194 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,15 @@ #define WAIT_IDLE 0xff +#define FIRMWARE_IN_THE_KERNEL + +#ifdef FIRMWARE_IN_THE_KERNEL #include "yss225.c" +static const struct firmware yss225_registers_firmware = { + .data = (u8 *)yss225_registers, + .size = sizeof yss225_registers +}; +#endif static int wavefront_fx_idle (snd_wavefront_t *dev) @@ -248,25 +257,47 @@ int __devinit snd_wavefront_fx_start (snd_wavefront_t *dev) { unsigned int i; + int err; + const struct firmware *firmware; if (dev->fx_initialized) return 0; - for (i = 0; i < ARRAY_SIZE(yss225_registers); ++i) { - if (yss225_registers[i].addr >= 8 && - yss225_registers[i].addr < 16) { - outb(yss225_registers[i].data, - yss225_registers[i].addr + dev->base); - } else if (yss225_registers[i].addr == WAIT_IDLE) { - if (!wavefront_fx_idle(dev)) - return -1; + err = request_firmware(&firmware, "yamaha/yss225_registers.bin", + dev->card->dev); + if (err < 0) { +#ifdef FIRMWARE_IN_THE_KERNEL + firmware = &yss225_registers_firmware; +#else + err = -1; + goto out; +#endif + } + + for (i = 0; i + 1 < firmware->size; i += 2) { + if (firmware->data[i] >= 8 && firmware->data[i] < 16) { + outb(firmware->data[i + 1], + dev->base + firmware->data[i]); + } else if (firmware->data[i] == WAIT_IDLE) { + if (!wavefront_fx_idle(dev)) { + err = -1; + goto out; + } } else { snd_printk(KERN_ERR "invalid address" " in register data\n"); - return -1; + err = -1; + goto out; } } dev->fx_initialized = 1; - return (0); + err = 0; + +out: +#ifdef FIRMWARE_IN_THE_KERNEL + if (firmware != &yss225_registers_firmware) +#endif + release_firmware(firmware); + return err; } -- cgit v1.2.3-59-g8ed1b From 2493a6d18b1f5df59c7bcfeefcbde70bee146490 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 6 Nov 2006 09:24:29 +0100 Subject: [ALSA] korg1212: add request_firmware() Load the DSP code using request_firmware(), if possible, instead of using the built-in blob. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/Kconfig | 1 + sound/pci/korg1212/korg1212.c | 45 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index fcbf9673db62..7573997af760 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -576,6 +576,7 @@ config SND_INTEL8X0M config SND_KORG1212 tristate "Korg 1212 IO" depends on SND + select FW_LOADER select SND_PCM help Say Y here to include support for Korg 1212IO soundcards. diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 345eefeedb39..b4e98f36229b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -263,7 +264,15 @@ enum MonitorModeSelector { #define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement // from the card after sending a command. +#define FIRMWARE_IN_THE_KERNEL + +#ifdef FIRMWARE_IN_THE_KERNEL #include "korg1212-firmware.h" +static const struct firmware static_dsp_code = { + .data = (u8 *)dspCode, + .size = sizeof dspCode +}; +#endif enum ClockSourceIndex { K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz @@ -345,8 +354,6 @@ struct snd_korg1212 { struct snd_dma_buffer dma_rec; struct snd_dma_buffer dma_shared; - u32 dspCodeSize; - u32 DataBufsSize; struct KorgAudioBuffer * playDataBufsPtr; @@ -1223,8 +1230,6 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212) snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); - memcpy(korg1212->dma_dsp.area, dspCode, korg1212->dspCodeSize); - rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, UpperWordSwap(korg1212->dma_dsp.addr), 0, 0, 0); @@ -2156,6 +2161,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev * unsigned int i; unsigned ioport_size, iomem_size, iomem2_size; struct snd_korg1212 * korg1212; + const struct firmware *dsp_code; static struct snd_device_ops ops = { .dev_free = snd_korg1212_dev_free, @@ -2329,8 +2335,6 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev * #endif // K1212_LARGEALLOC - korg1212->dspCodeSize = sizeof (dspCode); - korg1212->VolumeTablePhy = korg1212->sharedBufferPhy + offsetof(struct KorgSharedBuffer, volumeData); korg1212->RoutingTablePhy = korg1212->sharedBufferPhy + @@ -2338,17 +2342,40 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev * korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy + offsetof(struct KorgSharedBuffer, AdatTimeCode); + err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev); + if (err < 0) { + release_firmware(dsp_code); +#ifdef FIRMWARE_IN_THE_KERNEL + dsp_code = &static_dsp_code; +#else + snd_printk(KERN_ERR "firmware not available\n"); + snd_korg1212_free(korg1212); + return err; +#endif + } + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), - korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { - snd_printk(KERN_ERR "korg1212: can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + dsp_code->size, &korg1212->dma_dsp) < 0) { + snd_printk(KERN_ERR "korg1212: can not allocate dsp code memory (%d bytes)\n", dsp_code->size); snd_korg1212_free(korg1212); +#ifdef FIRMWARE_IN_THE_KERNEL + if (dsp_code != &static_dsp_code) +#endif + release_firmware(dsp_code); return -ENOMEM; } K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", - korg1212->dma_dsp.area, korg1212->dma_dsp.addr, korg1212->dspCodeSize, + korg1212->dma_dsp.area, korg1212->dma_dsp.addr, dsp_code->size, stateName[korg1212->cardState]); + memcpy(korg1212->dma_dsp.area, dsp_code->data, dsp_code->size); + +#ifdef FIRMWARE_IN_THE_KERNEL + if (dsp_code != &static_dsp_code) +#endif + release_firmware(dsp_code); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); if (rc) -- cgit v1.2.3-59-g8ed1b From 81d7724a8ee84693befbd60d730199ffb3988f29 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 6 Nov 2006 09:26:41 +0100 Subject: [ALSA] maestro3: add request_firmware() Load the ASSP codes using request_firmware(), if possible, instead of using the built-in blobs. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/Kconfig | 1 + sound/pci/maestro3.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 7573997af760..1bcfb3aac18d 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -587,6 +587,7 @@ config SND_KORG1212 config SND_MAESTRO3 tristate "ESS Allegro/Maestro3" depends on SND + select FW_LOADER select SND_AC97_CODEC help Say Y here to include support for soundcards based on ESS Maestro 3 diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 6efe6d5ade1e..053ea4fdbffd 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include MODULE_AUTHOR("Zach Brown , Takashi Iwai "); MODULE_DESCRIPTION("ESS Maestro3 PCI"); @@ -864,6 +866,9 @@ struct snd_m3 { #ifdef CONFIG_PM u16 *suspend_mem; #endif + + const struct firmware *assp_kernel_image; + const struct firmware *assp_minisrc_image; }; /* @@ -2132,6 +2137,10 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) } +#define FIRMWARE_IN_THE_KERNEL + +#ifdef FIRMWARE_IN_THE_KERNEL + /* * DSP Code images */ @@ -2260,6 +2269,30 @@ static const u16 assp_minisrc_image[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; +static const struct firmware assp_kernel = { + .data = (u8 *)assp_kernel_image, + .size = sizeof assp_kernel_image +}; +static const struct firmware assp_minisrc = { + .data = (u8 *)assp_minisrc_image, + .size = sizeof assp_minisrc_image +}; + +#endif /* FIRMWARE_IN_THE_KERNEL */ + +#ifdef __LITTLE_ENDIAN +static inline void snd_m3_convert_from_le(const struct firmware *fw) { } +#else +static void snd_m3_convert_from_le(const struct firmware *fw) +{ + int i; + u16 *data = (u16 *)fw->data; + + for (i = 0; i < fw->size / 2; ++i) + le16_to_cpus(&data[i]); +} +#endif + /* * initialize ASSP @@ -2274,6 +2307,7 @@ static const u16 minisrc_lpf[MINISRC_LPF_LEN] = { static void snd_m3_assp_init(struct snd_m3 *chip) { unsigned int i; + u16 *data; /* zero kernel data */ for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) @@ -2291,10 +2325,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip) KDATA_DMA_XFER0); /* write kernel into code memory.. */ - for (i = 0 ; i < ARRAY_SIZE(assp_kernel_image); i++) { + data = (u16 *)chip->assp_kernel_image->data; + for (i = 0 ; i * 2 < chip->assp_kernel_image->size; i++) { snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, - REV_B_CODE_MEMORY_BEGIN + i, - assp_kernel_image[i]); + REV_B_CODE_MEMORY_BEGIN + i, data[i]); } /* @@ -2303,10 +2337,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip) * drop it there. It seems that the minisrc doesn't * need vectors, so we won't bother with them.. */ - for (i = 0; i < ARRAY_SIZE(assp_minisrc_image); i++) { + data = (u16 *)chip->assp_minisrc_image->data; + for (i = 0; i * 2 < chip->assp_minisrc_image->size; i++) { snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, - 0x400 + i, - assp_minisrc_image[i]); + 0x400 + i, data[i]); } /* @@ -2553,6 +2587,15 @@ static int snd_m3_free(struct snd_m3 *chip) if (chip->iobase) pci_release_regions(chip->pci); +#ifdef FIRMWARE_IN_THE_KERNEL + if (chip->assp_kernel_image != &assp_kernel) +#endif + release_firmware(chip->assp_kernel_image); +#ifdef FIRMWARE_IN_THE_KERNEL + if (chip->assp_minisrc_image != &assp_minisrc) +#endif + release_firmware(chip->assp_minisrc_image); + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -2744,6 +2787,30 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, return -ENOMEM; } + err = request_firmware(&chip->assp_kernel_image, + "ess/maestro3_assp_kernel.fw", &pci->dev); + if (err < 0) { +#ifdef FIRMWARE_IN_THE_KERNEL + chip->assp_kernel_image = &assp_kernel; +#else + snd_m3_free(chip); + return err; +#endif + } else + snd_m3_convert_from_le(chip->assp_kernel_image); + + err = request_firmware(&chip->assp_minisrc_image, + "ess/maestro3_assp_minisrc.fw", &pci->dev); + if (err < 0) { +#ifdef FIRMWARE_IN_THE_KERNEL + chip->assp_minisrc_image = &assp_minisrc; +#else + snd_m3_free(chip); + return err; +#endif + } else + snd_m3_convert_from_le(chip->assp_minisrc_image); + if ((err = pci_request_regions(pci, card->driver)) < 0) { snd_m3_free(chip); return err; -- cgit v1.2.3-59-g8ed1b From 9174140cf383c56bdcabb4caf9c99c5ac8f3fdd7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2006 14:45:42 +0100 Subject: [ALSA] hda-codec - Fix model for ASUS M2N-MX Add a proper model (3stack) for ASUS M2N-MX with AD1986A codec. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_analog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 5e63123f70b8..9260560303ee 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -800,6 +800,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb, .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */ + { .pci_subvendor = 0x1043, .pci_subdevice = 0x8234, + .config = AD1986A_3STACK }, /* ASUS M2N-MX */ { .pci_subvendor = 0x17aa, .pci_subdevice = 0x1017, .config = AD1986A_3STACK }, /* Lenovo A60 desktop */ { .modelname = "laptop", .config = AD1986A_LAPTOP }, -- cgit v1.2.3-59-g8ed1b From 54bf5dd9ccd8c37830d7dae0737466e8fda018aa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2006 15:38:55 +0100 Subject: [ALSA] hdspm - Fix printk warnings sound/pci/rme9652/hdspm.c: In function 'snd_hdspm_hw_params': sound/pci/rme9652/hdspm.c:3681: warning: format '%08X' expects type 'unsigned int', but argument 4 has type 'unsigned char *' sound/pci/rme9652/hdspm.c:3692: warning: format '%08X' expects type 'unsigned int', but argument 4 has type 'unsigned char *' Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/rme9652/hdspm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 3d3a4ce3a35e..e0215aca1193 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3678,7 +3678,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, hdspm->playback_buffer = (unsigned char *) substream->runtime->dma_area; - snd_printdd("Allocated sample buffer for playback at 0x%08X\n", + snd_printdd("Allocated sample buffer for playback at %p\n", hdspm->playback_buffer); } else { hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn, @@ -3689,7 +3689,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, hdspm->capture_buffer = (unsigned char *) substream->runtime->dma_area; - snd_printdd("Allocated sample buffer for capture at 0x%08X\n", + snd_printdd("Allocated sample buffer for capture at %p\n", hdspm->capture_buffer); } /* -- cgit v1.2.3-59-g8ed1b From b373bdebf57e2ac7994d9be3a68fd5507515caef Mon Sep 17 00:00:00 2001 From: "Andrew L. Neporada" Date: Tue, 7 Nov 2006 11:37:08 +0100 Subject: [ALSA] hda-codec - Clevo M540JE, M550JE laptops (Nvidia MCP51 chipset, ALC883 codec) We need to enable External Amplifier on this laptops. This patch basicly adds laptop-eapd model to ALC883 codec. Signed-off-by: Andrew L. Neporada Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 30 +++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index e4255f69b300..8a254e2fe315 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -822,6 +822,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. medion Medion Laptops targa-dig Targa/MSI targa-2ch-dig Targs/MSI with 2-channel + laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE) auto auto-config reading BIOS (default) ALC861/660 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 990714e2bcb3..b1e8cd8961de 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -125,6 +125,7 @@ enum { ALC888_DEMO_BOARD, ALC883_ACER, ALC883_MEDION, + ALC883_LAPTOP_EAPD, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -4540,7 +4541,7 @@ static struct hda_verb alc882_init_verbs[] = { static struct hda_verb alc882_eapd_verbs[] = { /* change to EAPD mode */ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, { } }; @@ -4998,6 +4999,13 @@ static struct hda_channel_mode alc883_sixstack_modes[2] = { { 8, alc883_sixstack_ch8_init }, }; +static struct hda_verb alc883_medion_eapd_verbs[] = { + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + { } +}; + /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b */ @@ -5471,6 +5479,9 @@ static struct hda_board_config alc883_cfg_tbl[] = { .config = ALC883_ACER }, { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, .modelname = "medion", .config = ALC883_MEDION }, + { .modelname = "laptop-eapd", .config = ALC883_LAPTOP_EAPD }, + { .pci_subvendor = 0x1558, .pci_subdevice = 0, + .config = ALC883_LAPTOP_EAPD }, /* Clevo */ { .modelname = "auto", .config = ALC883_AUTO }, {} }; @@ -5591,7 +5602,7 @@ static struct alc_config_preset alc883_presets[] = { .mixers = { alc883_fivestack_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, - alc882_eapd_verbs }, + alc883_medion_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), @@ -5599,8 +5610,19 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, - } - + }, + [ALC883_LAPTOP_EAPD] = { + .mixers = { alc883_base_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, }; -- cgit v1.2.3-59-g8ed1b From bd903b6ed7fb107e122682db5ac8aaa323ab84c9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 9 Nov 2006 16:35:01 +0100 Subject: [ALSA] ASoC - mixer name changes for older OSS app support This patch suggested by Richard Purdie changes the names of some WM8731 and WM8750 mixers so that they will be recognised by some older OSS mixer apps. Changes:- o WM8731 Playback changed to Master Playback o WM8750 Out1 changed to Headphone o WM8750 Out2 changed to Speaker Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8731.c | 6 ++++-- sound/soc/codecs/wm8750.c | 12 +++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 412291241ece..8151b45a280c 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -310,8 +310,10 @@ static const struct soc_enum wm8731_enum[] = { static const struct snd_kcontrol_new wm8731_snd_controls[] = { -SOC_DOUBLE_R("Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, 0, 127, 0), -SOC_DOUBLE_R("Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 7, 1, 0), +SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, + 0, 127, 0), +SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, + 7, 1, 0), SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0), SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index c5d13a9454d9..4cc85128dc59 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -444,9 +444,9 @@ SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0), SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1), -SOC_DOUBLE_R("Out1 Playback ZC Switch", WM8750_LOUT1V, +SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V, WM8750_ROUT1V, 7, 1, 0), -SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8750_LOUT2V, +SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V, WM8750_ROUT2V, 7, 1, 0), SOC_ENUM("Playback De-emphasis", wm8750_enum[15]), @@ -487,7 +487,7 @@ SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0), SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0), SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0), -SOC_SINGLE("Right Out2 Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0), +SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0), /* Unimplemented */ /* ADCDAC Bit 0 - ADCHPD */ @@ -514,8 +514,10 @@ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1, SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0), -SOC_DOUBLE_R("Out1 Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V, 0, 127, 0), -SOC_DOUBLE_R("Out2 Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V, 0, 127, 0), +SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V, + 0, 127, 0), +SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V, + 0, 127, 0), SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0), -- cgit v1.2.3-59-g8ed1b From 56255060ea51984e728223d8056b3faaba0dadf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Nov 2006 16:47:26 +0100 Subject: [ALSA] ice1724 - Add support of M-Audio Audiophile 192 Added the (experimental) support of M-Audio Audiophile 192 board. Currently, the analog and the digital playbacks seem working fine. The inputs seem not working as far as I've tested yet. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 +- sound/pci/ice1712/revo.c | 204 ++++++++++++++++++++++++ sound/pci/ice1712/revo.h | 5 +- 3 files changed, 210 insertions(+), 2 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 8a254e2fe315..3f5f6900bbf9 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -982,6 +982,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards. * MidiMan M Audio Revolution 5.1 * MidiMan M Audio Revolution 7.1 + * MidiMan M Audio Audiophile 192 * AMP Ltd AUDIO2000 * TerraTec Aureon 5.1 Sky * TerraTec Aureon 7.1 Space @@ -1001,7 +1002,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. model - Use the given board model, one of the following: revo51, revo71, amp2000, prodigy71, prodigy71lt, - prodigy192, aureon51, aureon71, universe, + prodigy192, aureon51, aureon71, universe, ap192, k8x800, phase22, phase28, ms300, av710 This module supports multiple cards and autoprobe. diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 233e9a5a2e70..0e578aa38af2 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -303,6 +303,181 @@ static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = { static struct snd_pt2258 ptc_revo51_volume; +/* AK4358 for AP192 DAC, AK5385A for ADC */ +static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +{ + struct snd_ice1712 *ice = ak->private_data[0]; + + revo_set_rate_val(ak, rate); + +#if 1 /* FIXME: do we need this procedure? */ + /* reset DFS pin of AK5385A for ADC, too */ + /* DFS0 (pin 18) -- GPIO10 pin 77 */ + snd_ice1712_save_gpio_status(ice); + snd_ice1712_gpio_write_bits(ice, 1 << 10, + rate > 48000 ? (1 << 10) : 0); + snd_ice1712_restore_gpio_status(ice); +#endif +} + +static struct snd_akm4xxx_dac_channel ap192_dac[] = { + AK_DAC("PCM Playback Volume", 2) +}; + +static struct snd_akm4xxx akm_ap192 __devinitdata = { + .type = SND_AK4358, + .num_dacs = 2, + .ops = { + .set_rate_val = ap192_set_rate_val + }, + .dac_info = ap192_dac, +}; + +static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { + .caddr = 2, + .cif = 0, + .data_mask = VT1724_REVO_CDOUT, + .clk_mask = VT1724_REVO_CCLK, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3, + .cs_addr = VT1724_REVO_CS3, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3, + .add_flags = VT1724_REVO_CCLK, /* high at init */ + .mask_flags = 0, +}; + +#if 0 +/* FIXME: ak4114 makes the sound much lower due to some confliction, + * so let's disable it right now... + */ +#define BUILD_AK4114_AP192 +#endif + +#ifdef BUILD_AK4114_AP192 +/* AK4114 support on Audiophile 192 */ +/* CDTO (pin 32) -- GPIO2 pin 52 + * CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358) + * CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358) + * CSN (pin 35) -- GPIO7 pin 59 + */ +#define AK4114_ADDR 0x00 + +static void write_data(struct snd_ice1712 *ice, unsigned int gpio, + unsigned int data, int idx) +{ + for (; idx >= 0; idx--) { + /* drop clock */ + gpio &= ~VT1724_REVO_CCLK; + snd_ice1712_gpio_write(ice, gpio); + udelay(1); + /* set data */ + if (data & (1 << idx)) + gpio |= VT1724_REVO_CDOUT; + else + gpio &= ~VT1724_REVO_CDOUT; + snd_ice1712_gpio_write(ice, gpio); + udelay(1); + /* raise clock */ + gpio |= VT1724_REVO_CCLK; + snd_ice1712_gpio_write(ice, gpio); + udelay(1); + } +} + +static unsigned char read_data(struct snd_ice1712 *ice, unsigned int gpio, + int idx) +{ + unsigned char data = 0; + + for (; idx >= 0; idx--) { + /* drop clock */ + gpio &= ~VT1724_REVO_CCLK; + snd_ice1712_gpio_write(ice, gpio); + udelay(1); + /* read data */ + if (snd_ice1712_gpio_read(ice) & VT1724_REVO_CDIN) + data |= (1 << idx); + udelay(1); + /* raise clock */ + gpio |= VT1724_REVO_CCLK; + snd_ice1712_gpio_write(ice, gpio); + udelay(1); + } + return data; +} + +static unsigned char ap192_4wire_start(struct snd_ice1712 *ice) +{ + unsigned int tmp; + + snd_ice1712_save_gpio_status(ice); + tmp = snd_ice1712_gpio_read(ice); + tmp |= VT1724_REVO_CCLK; /* high at init */ + tmp |= VT1724_REVO_CS0; + tmp &= ~VT1724_REVO_CS3; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + return tmp; +} + +static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp) +{ + tmp |= VT1724_REVO_CS3; + tmp |= VT1724_REVO_CS0; + snd_ice1712_gpio_write(ice, tmp); + udelay(1); + snd_ice1712_restore_gpio_status(ice); +} + +static void ap192_ak4114_write(void *private_data, unsigned char addr, + unsigned char data) +{ + struct snd_ice1712 *ice = private_data; + unsigned int tmp, addrdata; + + tmp = ap192_4wire_start(ice); + addrdata = (AK4114_ADDR << 6) | 0x20 | (addr & 0x1f); + addrdata = (addrdata << 8) | data; + write_data(ice, tmp, addrdata, 15); + ap192_4wire_finish(ice, tmp); +} + +static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr) +{ + struct snd_ice1712 *ice = private_data; + unsigned int tmp; + unsigned char data; + + tmp = ap192_4wire_start(ice); + write_data(ice, tmp, (AK4114_ADDR << 6) | (addr & 0x1f), 7); + data = read_data(ice, tmp, 7); + ap192_4wire_finish(ice, tmp); + return data; +} + +static int ap192_ak4114_init(struct snd_ice1712 *ice) +{ + static unsigned char ak4114_init_vals[] = { + AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1, + AK4114_DIF_I24I2S, + AK4114_TX1E, + AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1), + 0, + 0 + }; + static unsigned char ak4114_init_txcsb[] = { + 0x41, 0x02, 0x2c, 0x00, 0x00 + }; + struct ak4114 *ak; + int err; + + return snd_ak4114_create(ice->card, + ap192_ak4114_read, + ap192_ak4114_write, + ak4114_init_vals, ak4114_init_txcsb, + ice, &ak); +} +#endif /* BUILD_AK4114_AP192 */ + static int __devinit revo_init(struct snd_ice1712 *ice) { struct snd_akm4xxx *ak; @@ -319,6 +494,10 @@ static int __devinit revo_init(struct snd_ice1712 *ice) ice->num_total_dacs = 6; ice->num_total_adcs = 2; break; + case VT1724_SUBDEVICE_AUDIOPHILE192: + ice->num_total_dacs = 2; + ice->num_total_adcs = 2; + break; default: snd_BUG(); return -EINVAL; @@ -356,6 +535,14 @@ static int __devinit revo_init(struct snd_ice1712 *ice) snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); break; + case VT1724_SUBDEVICE_AUDIOPHILE192: + ice->akm_codecs = 1; + err = snd_ice1712_akm4xxx_init(ak, &akm_ap192, &akm_ap192_priv, + ice); + if (err < 0) + return err; + + break; } return 0; @@ -380,6 +567,16 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; break; + case VT1724_SUBDEVICE_AUDIOPHILE192: + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; +#ifdef BUILD_AK4114_AP192 + err = ap192_ak4114_init(ice); + if (err < 0) + return err; +#endif + break; } return 0; } @@ -400,5 +597,12 @@ struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = { .chip_init = revo_init, .build_controls = revo_add_controls, }, + { + .subvendor = VT1724_SUBDEVICE_AUDIOPHILE192, + .name = "M Audio Audiophile192", + .model = "ap192", + .chip_init = revo_init, + .build_controls = revo_add_controls, + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h index c70adaf017c1..a3ba425911cc 100644 --- a/sound/pci/ice1712/revo.h +++ b/sound/pci/ice1712/revo.h @@ -26,10 +26,12 @@ #define REVO_DEVICE_DESC \ "{MidiMan M Audio,Revolution 7.1},"\ - "{MidiMan M Audio,Revolution 5.1}," + "{MidiMan M Audio,Revolution 5.1},"\ + "{MidiMan M Audio,Audiophile 192}," #define VT1724_SUBDEVICE_REVOLUTION71 0x12143036 #define VT1724_SUBDEVICE_REVOLUTION51 0x12143136 +#define VT1724_SUBDEVICE_AUDIOPHILE192 0x12143236 /* entry point */ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[]; @@ -47,6 +49,7 @@ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[]; #define VT1724_REVO_CS2 0x40 /* surround AKM4355 CS (revo71) */ #define VT1724_REVO_I2C_DATA 0x40 /* I2C: PT 2258 SDA (on revo51) */ #define VT1724_REVO_I2C_CLOCK 0x80 /* I2C: PT 2258 SCL (on revo51) */ +#define VT1724_REVO_CS3 0x80 /* AK4114 for AP192 */ #define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ #endif /* __SOUND_REVO_H */ -- cgit v1.2.3-59-g8ed1b From 4c07c81832a9303eeb36afb8f76ad0594e66cf5e Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Sat, 11 Nov 2006 10:48:58 +0000 Subject: [ALSA] snd-emu10k1: Update Enum naming. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- sound/pci/emu10k1/emumixer.c | 92 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 5ceb8dd5cb38..9b7fd564343e 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -316,30 +316,30 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { - EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Switch", 0), - EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Switch", 1), - EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Switch", 2), - EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Switch", 3), - EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Switch", 4), - EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Switch", 5), - EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Switch", 6), - EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Switch", 7), - EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Switch", 8), - EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Switch", 9), - EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Switch", 0xa), - EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Switch", 0xb), - EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Switch", 0xc), - EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Switch", 0xd), - EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Switch", 0xe), - EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Switch", 0xf), - EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Switch", 0x10), - EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Switch", 0x11), - EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Switch", 0x12), - EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Switch", 0x13), - EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Switch", 0x14), - EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Switch", 0x15), - EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Switch", 0x16), - EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Switch", 0x17), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5), + EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6), + EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7), + EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8), + EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb), + EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc), + EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd), + EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe), + EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf), + EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10), + EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11), + EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12), + EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13), + EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14), + EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15), + EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16), + EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17), }; #define EMU1010_SOURCE_INPUT(xname,chid) \ @@ -353,28 +353,28 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { } static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = { - EMU1010_SOURCE_INPUT("DSP 0 Capture Switch", 0), - EMU1010_SOURCE_INPUT("DSP 1 Capture Switch", 1), - EMU1010_SOURCE_INPUT("DSP 2 Capture Switch", 2), - EMU1010_SOURCE_INPUT("DSP 3 Capture Switch", 3), - EMU1010_SOURCE_INPUT("DSP 4 Capture Switch", 4), - EMU1010_SOURCE_INPUT("DSP 5 Capture Switch", 5), - EMU1010_SOURCE_INPUT("DSP 6 Capture Switch", 6), - EMU1010_SOURCE_INPUT("DSP 7 Capture Switch", 7), - EMU1010_SOURCE_INPUT("DSP 8 Capture Switch", 8), - EMU1010_SOURCE_INPUT("DSP 9 Capture Switch", 9), - EMU1010_SOURCE_INPUT("DSP A Capture Switch", 0xa), - EMU1010_SOURCE_INPUT("DSP B Capture Switch", 0xb), - EMU1010_SOURCE_INPUT("DSP C Capture Switch", 0xc), - EMU1010_SOURCE_INPUT("DSP D Capture Switch", 0xd), - EMU1010_SOURCE_INPUT("DSP E Capture Switch", 0xe), - EMU1010_SOURCE_INPUT("DSP F Capture Switch", 0xf), - EMU1010_SOURCE_INPUT("DSP 10 Capture Switch", 0x10), - EMU1010_SOURCE_INPUT("DSP 11 Capture Switch", 0x11), - EMU1010_SOURCE_INPUT("DSP 12 Capture Switch", 0x12), - EMU1010_SOURCE_INPUT("DSP 13 Capture Switch", 0x13), - EMU1010_SOURCE_INPUT("DSP 14 Capture Switch", 0x14), - EMU1010_SOURCE_INPUT("DSP 15 Capture Switch", 0x15), + EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0), + EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1), + EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2), + EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3), + EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4), + EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5), + EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6), + EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7), + EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8), + EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9), + EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa), + EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb), + EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc), + EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd), + EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe), + EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf), + EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10), + EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11), + EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12), + EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13), + EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14), + EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15), }; -- cgit v1.2.3-59-g8ed1b From e6327cf90b1e5e849ac87fbdaee7822a64b6ff56 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Sat, 11 Nov 2006 10:52:06 +0000 Subject: [ALSA] snd-ca0106: Updated Enum control names. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- sound/pci/ca0106/ca0106_mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 9855f528ea78..bd2a054c673b 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -539,14 +539,14 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Capture Source", + .name = "Digital Source Capture Enum", .info = snd_ca0106_capture_source_info, .get = snd_ca0106_capture_source_get, .put = snd_ca0106_capture_source_put }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", + .name = "Analog Source Capture Enum", .info = snd_ca0106_i2c_capture_source_info, .get = snd_ca0106_i2c_capture_source_get, .put = snd_ca0106_i2c_capture_source_put -- cgit v1.2.3-59-g8ed1b From c9b443d4fdf4e84ce1f40e1f507c313f3a8a8294 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Tue, 14 Nov 2006 12:13:39 +0100 Subject: [ALSA] Add Conexant audio support to the HD Audio driver This driver adds limited support for the Conexant 5045 and 5047 HD Audio codecs. Some issues still need to be resolved. The code is based primarily on code from the Analog Devices AD1981 support and the Realtek ALC260 support. Some code came from the original code developed by Alex Pototskiy (see alsa bugtracker 2485). Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 14 + sound/pci/hda/Makefile | 10 +- sound/pci/hda/hda_patch.h | 3 + sound/pci/hda/patch_conexant.c | 1298 +++++++++++++++++++++++ 4 files changed, 1324 insertions(+), 1 deletion(-) create mode 100644 sound/pci/hda/patch_conexant.c diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 3f5f6900bbf9..658b25d1545d 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -862,6 +862,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. laptop 3-jack with hp-jack automute laptop-dig ditto with SPDIF auto auto-config reading BIOS (default) + + Conexant 5045 + laptop Laptop config + test for testing/debugging purpose, almost all controls + can be adjusted. Appearing only when compiled with + $CONFIG_SND_DEBUG=y + + Conexant 5047 + laptop Basic Laptop config + laptop-hp Laptop config for some HP models (subdevice 30A5) + laptop-eapd Laptop config with EAPD support + test for testing/debugging purpose, almost all controls + can be adjusted. Appearing only when compiled with + $CONFIG_SND_DEBUG=y STAC9200/9205/9220/9221/9254 ref Reference board diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index dbacba6177db..148140bb86bd 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,5 +1,13 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o patch_atihdmi.o +snd-hda-codec-objs := hda_codec.o \ + hda_generic.o \ + patch_realtek.o \ + patch_cmedia.o \ + patch_analog.o \ + patch_sigmatel.o \ + patch_si3054.o \ + patch_atihdmi.o \ + patch_conexant.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index 0b668793face..5904ecd88a50 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -14,6 +14,8 @@ extern struct hda_codec_preset snd_hda_preset_sigmatel[]; extern struct hda_codec_preset snd_hda_preset_si3054[]; /* ATI HDMI codecs */ extern struct hda_codec_preset snd_hda_preset_atihdmi[]; +/* Conexant audio codec */ +extern struct hda_codec_preset snd_hda_preset_conexant[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, @@ -22,5 +24,6 @@ static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_sigmatel, snd_hda_preset_si3054, snd_hda_preset_atihdmi, + snd_hda_preset_conexant, NULL }; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c new file mode 100644 index 000000000000..9b69b62a550e --- /dev/null +++ b/sound/pci/hda/patch_conexant.c @@ -0,0 +1,1298 @@ +/* + * HD audio interface patch for Conexant HDA audio codec + * + * Copyright (c) 2006 Pototskiy Akex + * Takashi Iwai + * Tobin Davis + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + +#define CXT_PIN_DIR_IN 0x00 +#define CXT_PIN_DIR_OUT 0x01 +#define CXT_PIN_DIR_INOUT 0x02 +#define CXT_PIN_DIR_IN_NOMICBIAS 0x03 +#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04 + +#define CONEXANT_HP_EVENT 0x37 +#define CONEXANT_MIC_EVENT 0x38 + + + +struct conexant_spec { + + struct snd_kcontrol_new *mixers[5]; + int num_mixers; + + const struct hda_verb *init_verbs[5]; /* initialization verbs + * don't forget NULL + * termination! + */ + unsigned int num_init_verbs; + + /* playback */ + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ + unsigned int cur_eapd; + unsigned int need_dac_fix; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ + + /* capture source */ + const struct hda_input_mux *input_mux; + hda_nid_t *capsrc_nids; + unsigned int cur_mux[3]; + + /* channel model */ + const struct hda_channel_mode *channel_mode; + int num_channel_mode; + + /* PCM information */ + struct hda_pcm pcm_rec[2]; /* used in build_pcms() */ + + struct mutex amp_mutex; /* PCM volume/mute control mutex */ + unsigned int spdif_route; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + unsigned int num_kctl_alloc, num_kctl_used; + struct snd_kcontrol_new *kctl_alloc; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[4]; + +}; + +static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +static int conexant_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 conexant_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, + format, substream); +} + +static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + 0, 0, 0); + return 0; +} + + + +static struct hda_pcm_stream conexant_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .open = conexant_playback_pcm_open, + .prepare = conexant_playback_pcm_prepare, + .cleanup = conexant_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream conexant_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = conexant_capture_pcm_prepare, + .cleanup = conexant_capture_pcm_cleanup + }, +}; + + +static struct hda_pcm_stream conexant_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .open = conexant_dig_playback_pcm_open, + .close = conexant_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream conexant_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static int conexant_build_pcms(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "CONEXANT Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + + if (spec->multiout.dig_out_nid) { + info++; + codec->num_pcms++; + info->name = "Conexant Digital"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + conexant_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + conexant_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->dig_in_nid; + } + } + + return 0; +} + +static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} + +static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + spec->capsrc_nids[adc_idx], + &spec->cur_mux[adc_idx]); +} + +static int conexant_init(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_init_verbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); + return 0; +} + +static void conexant_free(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int i; + + if (spec->kctl_alloc) { + for (i = 0; i < spec->num_kctl_used; i++) + kfree(spec->kctl_alloc[i].name); + kfree(spec->kctl_alloc); + } + + kfree(codec->spec); +} + +#ifdef CONFIG_PM +static int conexant_resume(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i; + + codec->patch_ops.init(codec); + for (i = 0; i < spec->num_mixers; i++) + snd_hda_resume_ctls(codec, spec->mixers[i]); + if (spec->multiout.dig_out_nid) + snd_hda_resume_spdif_out(codec); + if (spec->dig_in_nid) + snd_hda_resume_spdif_in(codec); + return 0; +} +#endif + +static int conexant_build_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int i; + int err; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); + if (err < 0) + return err; + } + return 0; +} + +static struct hda_codec_ops conexant_patch_ops = { + .build_controls = conexant_build_controls, + .build_pcms = conexant_build_pcms, + .init = conexant_init, + .free = conexant_free, +#ifdef CONFIG_PM + .resume = conexant_resume, +#endif +}; + +/* + * EAPD control + * the private value = nid | (invert << 8) + */ + +static int conexant_eapd_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 conexant_eapd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + int invert = (kcontrol->private_value >> 8) & 1; + if (invert) + ucontrol->value.integer.value[0] = !spec->cur_eapd; + else + ucontrol->value.integer.value[0] = spec->cur_eapd; + return 0; +} + +static int conexant_eapd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + int invert = (kcontrol->private_value >> 8) & 1; + hda_nid_t nid = kcontrol->private_value & 0xff; + unsigned int eapd; + eapd = ucontrol->value.integer.value[0]; + if (invert) + eapd = !eapd; + if (eapd == spec->cur_eapd && !codec->in_resume) + return 0; + spec->cur_eapd = eapd; + snd_hda_codec_write(codec, nid, + 0, AC_VERB_SET_EAPD_BTLENABLE, + eapd ? 0x02 : 0x00); + return 1; +} + +static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, + spec->num_channel_mode); +} + +static int conexant_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + spec->multiout.max_channels); +} + +static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + &spec->multiout.max_channels); + if (err >= 0 && spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + return err; +} + +#define CXT_PIN_MODE(xname, nid, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = conexant_ch_mode_info, \ + .get = conexant_ch_mode_get, \ + .put = conexant_ch_mode_put, \ + .private_value = nid | (dir<<16) } + +static int cxt_gpio_data_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 cxt_gpio_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0x00); + + *valp = (val & mask) != 0; + return 0; +} + +static int cxt_gpio_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, + 0x00); + unsigned int old_data = gpio_data; + + /* Set/unset the masked GPIO bit(s) as needed */ + if (val == 0) + gpio_data &= ~mask; + else + gpio_data |= mask; + if (gpio_data == old_data && !codec->in_resume) + return 0; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data); + return 1; +} + +#define CXT_GPIO_DATA_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = cxt_gpio_data_info, \ + .get = cxt_gpio_data_get, \ + .put = cxt_gpio_data_put, \ + .private_value = nid | (mask<<16) } + +static int cxt_spdif_ctrl_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 cxt_spdif_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, 0x00); + + *valp = (val & mask) != 0; + return 0; +} + +static int cxt_spdif_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, + 0x00); + unsigned int old_data = ctrl_data; + + /* Set/unset the masked control bit(s) as needed */ + if (val == 0) + ctrl_data &= ~mask; + else + ctrl_data |= mask; + if (ctrl_data == old_data && !codec->in_resume) + return 0; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, + ctrl_data); + return 1; +} + +#define CXT_SPDIF_CTRL_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = cxt_spdif_ctrl_info, \ + .get = cxt_spdif_ctrl_get, \ + .put = cxt_spdif_ctrl_put, \ + .private_value = nid | (mask<<16) } + +/* Conexant 5045 specific */ + +static hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; +static hda_nid_t cxt5045_adc_nids[1] = { 0x1a }; +static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a }; +#define CXT5045_SPDIF_OUT 0x13 + + +static struct hda_input_mux cxt5045_capture_source = { + .num_items = 2, + .items = { + { "ExtMic", 0x1 }, + { "LineIn", 0x2 }, + } +}; + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + if (!conexant_eapd_put(kcontrol, ucontrol)) + return 0; + + /* toggle HP mute appropriately */ + snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0, + 0x80, spec->cur_eapd ? 0 : 0x80); + snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0, + 0x80, spec->cur_eapd ? 0 : 0x80); + return 1; +} + +/* bind volumes of both NID 0x10 and 0x11 */ +static int cxt5045_hp_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + return change; +} + + +/* mute internal speaker if HP is plugged */ +static void cxt5045_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); +} + +/* unsolicited event for HP jack sensing */ +static void cxt5045_hp_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + res >>= 26; + switch (res) { + case CONEXANT_HP_EVENT: + cxt5045_hp_automute(codec); + break; + } +} + +static struct snd_kcontrol_new cxt5045_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, + HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x17, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x17, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x02, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = cxt5045_hp_master_vol_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = conexant_eapd_info, + .get = conexant_eapd_get, + .put = cxt5045_hp_master_sw_put, + .private_value = 0x11, + }, + + {} +}; + +static struct hda_verb cxt5045_init_verbs[] = { + /* Line in, Mic */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, + /* HP, Amp */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x1A, AC_VERB_SET_CONNECT_SEL,0x01}, + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00}, + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03}, + /* Record selector: Front mic */ + {0x14, AC_VERB_SET_CONNECT_SEL,0x03}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, + /* SPDIF route: PCM */ + { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 }, + /* pin sensing on HP and Mic jacks */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + /* EAPD */ + {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */ + { } /* end */ +}; + +#ifdef CONFIG_SND_DEBUG +/* Test configuration for debugging, modelled after the ALC260 test + * configuration. + */ +static struct hda_input_mux cxt5045_test_capture_source = { + .num_items = 5, + .items = { + { "MIXER", 0x0 }, + { "MIC1 pin", 0x1 }, + { "LINE1 pin", 0x2 }, + { "HP-OUT pin", 0x3 }, + { "CD pin", 0x4 }, + }, +}; + +static struct snd_kcontrol_new cxt5045_test_mixer[] = { + + /* Output controls */ + HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x19, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("OutAmp-1 Switch", 0x19,0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT), + CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT), + + /* Loopback mixer controls */ + HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x17, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("MIC1 Playback Switch", 0x17, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("LINE loopback Playback Volume", 0x17, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("LINE loopback Playback Switch", 0x17, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x17, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x17, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x04, HDA_INPUT), + + /* Controls for GPIO pins, assuming they exist and are configured as outputs */ + CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), +#if 0 /* limit this to one GPIO pin for now */ + CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), +#endif + CXT_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x13, 0x01), + + HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put, + }, + + { } /* end */ +}; + +static struct hda_verb cxt5045_test_init_verbs[] = { + /* Enable all GPIOs as outputs with an initial value of 0 */ + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, + + /* Enable retasking pins as output, initially without power amp */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* Disable digital (SPDIF) pins initially, but users can enable + * them via a mixer switch. In the case of SPDIF-out, this initverb + * payload also sets the generation to 0, output to be in "consumer" + * PCM format, copyright asserted, no pre-emphasis and no validity + * control. + */ + {0x13, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Unmute retasking pin widget output buffers since the default + * state appears to be output. As the pin mode is changed by the + * user the pin mode control will take care of enabling the pin's + * input/output buffers as needed. + */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mute capture amp left and right */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + /* Set ADC connection select to match default mixer setting (mic1 + * pin) + */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + + { } +}; +#endif + + +/* initialize jack-sensing, too */ +static int cxt5045_init(struct hda_codec *codec) +{ + conexant_init(codec); + cxt5045_hp_automute(codec); + return 0; +} + + +enum { + CXT5045_LAPTOP, +#ifdef CONFIG_SND_DEBUG + CXT5045_TEST, +#endif +}; + +static struct hda_board_config cxt5045_cfg_tbl[] = { + /* Laptops w/ EAPD support */ + { .modelname = "laptop", .config = CXT5045_LAPTOP }, + /* HP DV6000Z */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x30b7, + .config = CXT5045_LAPTOP }, +#ifdef CONFIG_SND_DEBUG + { .modelname = "test", .config = CXT5045_TEST }, +#endif + + {} +}; + +static int patch_cxt5045(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); + spec->multiout.dac_nids = cxt5045_dac_nids; + spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT; + spec->num_adc_nids = 1; + spec->adc_nids = cxt5045_adc_nids; + spec->capsrc_nids = cxt5045_capsrc_nids; + spec->input_mux = &cxt5045_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = cxt5045_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5045_init_verbs; + spec->spdif_route = 0; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + + board_config = snd_hda_check_board_config(codec, cxt5045_cfg_tbl); + switch (board_config) { + case CXT5045_LAPTOP: + spec->input_mux = &cxt5045_capture_source; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5045_init_verbs; + spec->mixers[0] = cxt5045_mixers; + codec->patch_ops.init = cxt5045_init; + break; +#ifdef CONFIG_SND_DEBUG + case CXT5045_TEST: + spec->input_mux = &cxt5045_test_capture_source; + spec->mixers[0] = cxt5045_test_mixer; + spec->init_verbs[0] = cxt5045_test_init_verbs; +#endif + } + return 0; +} + + +/* Conexant 5047 specific */ + +static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; +static hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; +static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; +#define CXT5047_SPDIF_OUT 0x11 + + +static struct hda_input_mux cxt5047_capture_source = { + .num_items = 2, + .items = { + { "ExtMic", 0x1 }, + { "IntMic", 0x2 }, + } +}; + +static struct hda_input_mux cxt5047_hp_capture_source = { + .num_items = 1, + .items = { + { "ExtMic", 0x1 }, + } +}; + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + if (!conexant_eapd_put(kcontrol, ucontrol)) + return 0; + + /* toggle HP mute appropriately */ + snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0, + 0x80, spec->cur_eapd ? 0 : 0x80); + snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0, + 0x80, spec->cur_eapd ? 0 : 0x80); + return 1; +} + +#if 0 +/* bind volumes of both NID 0x13 and 0x1d */ +static int cxt5047_hp_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + change |= snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + return change; +} +#endif + +/* mute internal speaker if HP is plugged */ +static void cxt5047_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x13, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0, + 0x80, present ? 0x80 : 0); +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5047_hp_automic(struct hda_codec *codec) +{ + static struct hda_verb mic_jack_on[] = { + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {} + }; + static struct hda_verb mic_jack_off[] = { + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {} + }; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x08, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) + snd_hda_sequence_write(codec, mic_jack_on); + else + snd_hda_sequence_write(codec, mic_jack_off); +} + +/* unsolicited event for HP jack sensing */ +static void cxt5047_hp_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + res >>= 26; + switch (res) { + case CONEXANT_HP_EVENT: + cxt5047_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5047_hp_automic(codec); + break; + } +} + +static struct snd_kcontrol_new cxt5047_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, + HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = conexant_eapd_info, + .get = conexant_eapd_get, + .put = cxt5047_hp_master_sw_put, + .private_value = 0x13, + }, + + {} +}; + +static struct snd_kcontrol_new cxt5047_hp_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, + HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = conexant_eapd_info, + .get = conexant_eapd_get, + .put = cxt5047_hp_master_sw_put, + .private_value = 0x13, + }, + { } /* end */ +}; + +static struct hda_verb cxt5047_init_verbs[] = { + /* Line in, Mic, Built-in Mic */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, + /* HP, Amp */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x1A, AC_VERB_SET_CONNECT_SEL,0x01}, + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00}, + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03}, + /* Record selector: Front mic */ + {0x12, AC_VERB_SET_CONNECT_SEL,0x03}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, + /* SPDIF route: PCM */ + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 }, + { } /* end */ +}; + +/* configuration for Toshiba Laptops */ +static struct hda_verb cxt5047_toshiba_init_verbs[] = { + {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */ + /* pin sensing on HP and Mic jacks */ + {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + {} +}; + +/* configuration for HP Laptops */ +static struct hda_verb cxt5047_hp_init_verbs[] = { + /* pin sensing on HP and Mic jacks */ + {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + {} +}; + +/* Test configuration for debugging, modelled after the ALC260 test + * configuration. + */ +#ifdef CONFIG_SND_DEBUG +static struct hda_input_mux cxt5047_test_capture_source = { + .num_items = 5, + .items = { + { "MIXER", 0x0 }, + { "LINE1 pin", 0x1 }, + { "MIC1 pin", 0x2 }, + { "MIC2 pin", 0x3 }, + { "CD pin", 0x4 }, + }, +}; + +static struct snd_kcontrol_new cxt5047_test_mixer[] = { + + /* Output only controls */ + HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("OutAmp-1 Switch", 0x10,0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("OutAmp-2 Volume", 0x1c, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("OutAmp-2 Switch", 0x1c, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("HeadPhone Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("HeadPhone Playback Switch", 0x13, 0x0, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + CXT_PIN_MODE("LINE1 pin mode", 0x14, CXT_PIN_DIR_INOUT), + CXT_PIN_MODE("MIC1 pin mode", 0x15, CXT_PIN_DIR_INOUT), + + /* Loopback mixer controls */ + HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x19, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("MIC1 Playback Switch", 0x19, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x19, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("MIC2 Playback Switch", 0x19, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("LINE Playback Volume", 0x19, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("LINE Playback Switch", 0x19, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x19, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x19, 0x04, HDA_INPUT), + + /* Controls for GPIO pins, assuming they exist and are configured as outputs */ + CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), +#if 0 /* limit this to one GPIO pin for now */ + CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), +#endif + CXT_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x18, 0x01), + + HDA_CODEC_VOLUME("Capture Volume", 0x19, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x19, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put, + }, + + { } /* end */ +}; + +static struct hda_verb cxt5047_test_init_verbs[] = { + /* Enable all GPIOs as outputs with an initial value of 0 */ + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, + + /* Enable retasking pins as output, initially without power amp */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* Disable digital (SPDIF) pins initially, but users can enable + * them via a mixer switch. In the case of SPDIF-out, this initverb + * payload also sets the generation to 0, output to be in "consumer" + * PCM format, copyright asserted, no pre-emphasis and no validity + * control. + */ + {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Ensure mic1, mic2, line1 pin widgets take input from the + * OUT1 sum bus when acting as an output. + */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Unmute retasking pin widget output buffers since the default + * state appears to be output. As the pin mode is changed by the + * user the pin mode control will take care of enabling the pin's + * input/output buffers as needed. + */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mute capture amp left and right */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + /* Set ADC connection select to match default mixer setting (mic1 + * pin) + */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ + + { } +}; +#endif + + +/* initialize jack-sensing, too */ +static int cxt5047_hp_init(struct hda_codec *codec) +{ + conexant_init(codec); + cxt5047_hp_automute(codec); + cxt5047_hp_automic(codec); + return 0; +} + + +enum { + CXT5047_LAPTOP, +#ifdef CONFIG_SND_DEBUG + CXT5047_TEST, +#endif + CXT5047_LAPTOP_HP, + CXT5047_LAPTOP_EAPD +}; + +static struct hda_board_config cxt5047_cfg_tbl[] = { + /* Laptops w/o EAPD support */ + { .modelname = "laptop", .config = CXT5047_LAPTOP }, + /*HP DV1000 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x30a0, + .config = CXT5047_LAPTOP }, + /*HP DV2000T/DV3000T */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x30b2, + .config = CXT5047_LAPTOP }, + /* Not all HP's are created equal */ + { .modelname = "laptop-hp", .config = CXT5047_LAPTOP_HP }, + /*HP DV5200TX/DV8000T / Compaq V5209US/V5204NR */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x30a5, + .config = CXT5047_LAPTOP_HP }, + /* Laptops with EAPD support */ + { .modelname = "laptop-eapd", .config = CXT5047_LAPTOP_EAPD }, + { .pci_subvendor = 0x1179, .pci_subdevice = 0xff31, + .config = CXT5047_LAPTOP_EAPD }, /* Toshiba P100 */ +#ifdef CONFIG_SND_DEBUG + { .modelname = "test", .config = CXT5047_TEST }, +#endif + + {} +}; + +static int patch_cxt5047(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids); + spec->multiout.dac_nids = cxt5047_dac_nids; + spec->multiout.dig_out_nid = CXT5047_SPDIF_OUT; + spec->num_adc_nids = 1; + spec->adc_nids = cxt5047_adc_nids; + spec->capsrc_nids = cxt5047_capsrc_nids; + spec->input_mux = &cxt5047_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = cxt5047_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5047_init_verbs; + spec->spdif_route = 0; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; + + board_config = snd_hda_check_board_config(codec, cxt5047_cfg_tbl); + switch (board_config) { + case CXT5047_LAPTOP: + break; + case CXT5047_LAPTOP_HP: + spec->input_mux = &cxt5047_hp_capture_source; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5047_hp_init_verbs; + spec->mixers[0] = cxt5047_hp_mixers; + codec->patch_ops.init = cxt5047_hp_init; + break; + case CXT5047_LAPTOP_EAPD: + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5047_toshiba_init_verbs; + break; +#ifdef CONFIG_SND_DEBUG + case CXT5047_TEST: + spec->input_mux = &cxt5047_test_capture_source; + spec->mixers[0] = cxt5047_test_mixer; + spec->init_verbs[0] = cxt5047_test_init_verbs; +#endif + } + return 0; +} + +struct hda_codec_preset snd_hda_preset_conexant[] = { + { .id = 0x14f15045, .name = "CXT5045", .patch = patch_cxt5045 }, + { .id = 0x14f15047, .name = "CXT5047", .patch = patch_cxt5047 }, + {} /* terminator */ +}; -- cgit v1.2.3-59-g8ed1b From d1f6754748a6523fcd35be7f4aaaf6fde5e5ca87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Nov 2006 12:30:52 +0100 Subject: [ALSA] hda-codec - Add support for Sony UX-90s Added the model entry (model=hippo) for Sony UX-90s with ALC262 codec. Although the device has no SPDIF output, the hippo model adds a PCM output, but it must be harmless. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 2 +- sound/pci/hda/patch_realtek.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 658b25d1545d..c54fd7c258d9 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -801,7 +801,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. fujitsu Fujitsu Laptop hp-bpc HP xw4400/6400/8400/9400 laptops benq Benq ED8 - hippo Hippo (ATI) with jack detection + hippo Hippo (ATI) with jack detection, Sony UX-90s hippo_1 Hippo (Benq) with jack detection basic fixed pin assignment w/o SPDIF auto auto-config reading BIOS (default) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b1e8cd8961de..8fc0fbb4f528 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6440,6 +6440,9 @@ static struct hda_board_config alc262_cfg_tbl[] = { { .modelname = "hippo", .pci_subvendor =0x1002, .pci_subdevice = 0x437b, .config = ALC262_HIPPO}, + { .modelname = "hippo", + .pci_subvendor = 0x104d, .pci_subdevice = 0x8203, + .config = ALC262_HIPPO }, /* Sony UX-90s */ { .modelname = "hippo_1", .pci_subvendor =0x17ff, .pci_subdevice = 0x058f, .config = ALC262_HIPPO_1}, -- cgit v1.2.3-59-g8ed1b From a2ee47026025a3d1a5c2eccf3b0aa6c9fb02b101 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Thu, 16 Nov 2006 16:24:35 +0100 Subject: [ALSA] hda-codec - Change Gigabyte K8N51 from 6stack to 6stack-digout This patch moves the entry for the Gigabyte K8N51 from the 6stack grouping to the 6stack-digout grouping, allowing for S/PDIF output functionality. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8fc0fbb4f528..7b4bac3567d5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2427,7 +2427,6 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST }, { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */ - { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST }, /* Gigabyte K8N51 */ { .modelname = "6stack-digout", .config = ALC880_6ST_DIG }, { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG }, @@ -2441,6 +2440,7 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */ { .pci_subvendor = 0x1509, .pci_subdevice = 0x925d, .config = ALC880_6ST_DIG }, /* FIC P4M-915GD1 */ { .pci_subvendor = 0x1695, .pci_subdevice = 0x4012, .config = ALC880_5ST_DIG }, /* Epox EP-5LDA+ GLi */ + { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST_DIG }, /* Gigabyte K8N51 */ { .modelname = "asus", .config = ALC880_ASUS }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG }, -- cgit v1.2.3-59-g8ed1b From 9dece1d74bd41f593cb1d9e387dc894dd826abf7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Nov 2006 17:12:49 +0100 Subject: [ALSA] hda-codec - Fix ALC861 connection of front-output Fix the wrongly set SET_CONNECTION verb for NID 0x0f of ALC861. The widget has only a single connection although the init verb sets to 0x01. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7b4bac3567d5..02ee6565b6e9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6922,7 +6922,7 @@ static struct hda_verb alc861_base_init_verbs[] = { /* port-E for HP out (front panel) */ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-F for mic-in (front panel) with vref */ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* port-G for CLFE (rear panel) */ @@ -6986,7 +6986,7 @@ static struct hda_verb alc861_threestack_init_verbs[] = { /* port-E for HP out (front panel) */ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-F for mic-in (front panel) with vref */ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* port-G for CLFE (rear panel) */ @@ -7046,7 +7046,7 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = { /* port-E for HP out (front panel) */ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80 /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-F for mic-in (front panel) with vref */ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* port-G for CLFE (rear panel) */ @@ -7108,7 +7108,7 @@ static struct hda_verb alc861_asus_init_verbs[] = { /* port-E for HP out (front panel) */ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* this has to be set to VREF80 */ /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, /* port-F for mic-in (front panel) with vref */ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* port-G for CLFE (rear panel) */ -- cgit v1.2.3-59-g8ed1b From e3a4050cdd7df05fba6512ac71c9360246e19ac4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Nov 2006 17:24:20 +0100 Subject: [ALSA] hda-codec - Add model for ASUS W3j laptop Added a proper model entry (model=laptop-eapd) for ASUS W3j laptop with AD1986A codec. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_analog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9260560303ee..9ce4c9f869b2 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -830,6 +830,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { .config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x12b3, .config = AD1986A_LAPTOP_EAPD }, /* ASUS V1j */ + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1302, + .config = AD1986A_LAPTOP_EAPD }, /* ASUS W3j */ { .pci_subvendor = 0x103c, .pci_subdevice = 0x30af, .config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */ { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066, -- cgit v1.2.3-59-g8ed1b From 761ccb24b4cad211295a5abe231f418ad97aac04 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 20 Nov 2006 12:02:56 +0100 Subject: [ALSA] hda-codec - Add support for Evesham Voyager C530RD laptops This patch adds support for the Evesham Voyager C530RD series laptops. So far, only playback has been tested, but microphone should also work. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 02ee6565b6e9..aeb408cbe1ef 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5480,6 +5480,8 @@ static struct hda_board_config alc883_cfg_tbl[] = { { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, .modelname = "medion", .config = ALC883_MEDION }, { .modelname = "laptop-eapd", .config = ALC883_LAPTOP_EAPD }, + { .pci_subvendor = 0x1071, .pci_subdevice = 0x8258, + .config = ALC883_LAPTOP_EAPD }, /* Evesham Voyager C530RD */ { .pci_subvendor = 0x1558, .pci_subdevice = 0, .config = ALC883_LAPTOP_EAPD }, /* Clevo */ { .modelname = "auto", .config = ALC883_AUTO }, -- cgit v1.2.3-59-g8ed1b From ddc2cec4dbec157ac7426111205d59ac28f887ee Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 20 Nov 2006 12:03:44 +0100 Subject: [ALSA] make sound/pci/hda/patch_sigmatel.c:stac92xx_dmic_labels[] static This patch makes the needlessly global stac92xx_dmic_labels[] static. Signed-off-by: Adrian Bunk Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_sigmatel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8f52372d66a2..1b428a1eafd0 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1201,7 +1201,7 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, } /* labels for dmic mux inputs */ -const char *stac92xx_dmic_labels[5] = { +static const char *stac92xx_dmic_labels[5] = { "Analog Inputs", "Digital Mic 1", "Digital Mic 2", "Digital Mic 3", "Digital Mic 4" }; -- cgit v1.2.3-59-g8ed1b From 14e1d357e4fed9577d349952b71ec7d81aad710c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 20 Nov 2006 16:35:18 +0100 Subject: [ALSA] atiixp - Add a parameter ac97_quirk Add an option to specify the AC'97 codec instead of probing. This is a fix for bugzilla #7467. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 6 ++++ sound/pci/atiixp.c | 45 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index c54fd7c258d9..d853a303ffcb 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -242,6 +242,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ac97_clock - AC'97 clock (default = 48000) ac97_quirk - AC'97 workaround for strange hardware See "AC97 Quirk Option" section below. + ac97_codec - Workaround to specify which AC'97 codec + instead of probing. If this works for you + file a bug with your `lspci -vn` output. + -2 -- Force probing. + -1 -- Default behavior. + 0-2 -- Use the specified codec. spdif_aclink - S/PDIF transfer over AC-link (default = 1) This module supports one card and autoprobe. diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 86710df71a8e..92df811d695d 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -45,6 +45,7 @@ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ static int ac97_clock = 48000; static char *ac97_quirk; static int spdif_aclink = 1; +static int ac97_codec = -1; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for ATI IXP controller."); @@ -54,6 +55,8 @@ module_param(ac97_clock, int, 0444); MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); module_param(ac97_quirk, charp, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); +module_param(ac97_codec, int, 0444); +MODULE_PARM_DESC(ac97_codec, "Specify codec instead of probing."); module_param(spdif_aclink, bool, 0444); MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link."); @@ -293,6 +296,22 @@ static struct pci_device_id snd_atiixp_ids[] = { MODULE_DEVICE_TABLE(pci, snd_atiixp_ids); +struct atiixp_quirk { + unsigned short subvendor; + unsigned short subdevice; + const char *name; + int ac97_codec; +}; + +static struct atiixp_quirk atiixp_quirks[] __devinitdata = { + { + .subvendor = 0x15bd, + .subdevice = 0x3100, + .name = "DFI RS482", + .ac97_codec = 0, + }, + { .subvendor = 0 } /* terminator */ +}; /* * lowlevel functions @@ -553,11 +572,37 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_ISR_CODEC2_NOT_READY) #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME) +static int ac97_probing_bugs(struct pci_dev *pci) +{ + int i = 0; + + while (atiixp_quirks[i].subvendor) { + if (pci->subsystem_vendor == atiixp_quirks[i].subvendor && + pci->subsystem_device == atiixp_quirks[i].subdevice) { + printk(KERN_INFO "Atiixp quirk for %s. " + "Forcing codec %d\n", atiixp_quirks[i].name, + atiixp_quirks[i].ac97_codec); + return atiixp_quirks[i].ac97_codec; + } + i++; + } + /* this hardware doesn't need workarounds. Probe for codec */ + return -1; +} + static int snd_atiixp_codec_detect(struct atiixp *chip) { int timeout; chip->codec_not_ready_bits = 0; + if (ac97_codec == -1) + ac97_codec = ac97_probing_bugs(chip->pci); + if (ac97_codec >= 0) { + chip->codec_not_ready_bits |= + CODEC_CHECK_BITS ^ (1 << (ac97_codec + 10)); + return 0; + } + atiixp_write(chip, IER, CODEC_CHECK_BITS); /* wait for the interrupts */ timeout = 50; -- cgit v1.2.3-59-g8ed1b From 5cd575290b4481b3a6ea307afed760df60d01cbc Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 20 Nov 2006 17:42:09 +0100 Subject: [ALSA] hda-codec - Add missing array to conexant driver This patch adds a missing array to the conexant driver. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_conexant.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 9b69b62a550e..7e7d0c110c4c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -552,6 +552,9 @@ static hda_nid_t cxt5045_adc_nids[1] = { 0x1a }; static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a }; #define CXT5045_SPDIF_OUT 0x13 +static struct hda_channel_mode cxt5045_modes[1] = { + { 2, NULL }, +}; static struct hda_input_mux cxt5045_capture_source = { .num_items = 2, @@ -842,6 +845,9 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5045_init_verbs; spec->spdif_route = 0; + spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes), + spec->channel_mode = cxt5045_modes, + codec->patch_ops = conexant_patch_ops; codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; @@ -873,6 +879,9 @@ static hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; #define CXT5047_SPDIF_OUT 0x11 +static struct hda_channel_mode cxt5047_modes[1] = { + { 2, NULL }, +}; static struct hda_input_mux cxt5047_capture_source = { .num_items = 2, @@ -1039,7 +1048,7 @@ static struct hda_verb cxt5047_init_verbs[] = { {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, /* HP, Amp */ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1A, AC_VERB_SET_CONNECT_SEL,0x01}, + {0x1A, AC_VERB_SET_CONNECT_SEL,0x03}, {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00}, {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, @@ -1111,9 +1120,9 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = { HDA_CODEC_VOLUME("CD Playback Volume", 0x19, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x19, 0x04, HDA_INPUT), +#if 0 /* Controls for GPIO pins, assuming they exist and are configured as outputs */ CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), -#if 0 /* limit this to one GPIO pin for now */ CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), @@ -1262,6 +1271,8 @@ static int patch_cxt5047(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5047_init_verbs; spec->spdif_route = 0; + spec->num_channel_mode = ARRAY_SIZE(cxt5047_modes), + spec->channel_mode = cxt5047_modes, codec->patch_ops = conexant_patch_ops; codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; -- cgit v1.2.3-59-g8ed1b From 0b51ba07e2e2866bfea40c5551a926dbefae64da Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 20 Nov 2006 17:50:17 +0100 Subject: [ALSA] make sound/core/control.c:snd_ctl_new() static Now that everyone uses snd_ctl_new1() and noone is using snd_ctl_new() anymore, we can make it static. Signed-off-by: Adrian Bunk Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 10 ---------- include/sound/control.h | 1 - sound/core/control.c | 5 ++--- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index ccd0a953953d..a319905c2c72 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -3690,16 +3690,6 @@ struct _snd_pcm_runtime { - - Here, the chip instance is retrieved via - snd_kcontrol_chip() macro. This macro - just accesses to kcontrol->private_data. The - kcontrol->private_data field is - given as the argument of snd_ctl_new() - (see the later subsection - Constructor). - - The value field is depending on the type of control as well as on info callback. For example, diff --git a/include/sound/control.h b/include/sound/control.h index 1de148b0fd94..f1361d6694ff 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -108,7 +108,6 @@ typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card, void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id); -struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol * kcontrol, unsigned int access); struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data); void snd_ctl_free_one(struct snd_kcontrol * kcontrol); int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol); diff --git a/sound/core/control.c b/sound/core/control.c index 67f09b8f85e4..42bcf2794b28 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -183,7 +183,8 @@ EXPORT_SYMBOL(snd_ctl_notify); * * Returns the pointer of the new instance, or NULL on failure. */ -struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access) +static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, + unsigned int access) { struct snd_kcontrol *kctl; unsigned int idx; @@ -201,8 +202,6 @@ struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int acce return kctl; } -EXPORT_SYMBOL(snd_ctl_new); - /** * snd_ctl_new1 - create a control instance from the template * @ncontrol: the initialization record -- cgit v1.2.3-59-g8ed1b From 69e134189763341560a5201c2eee9930eeb0b4f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Nov 2006 12:10:55 +0100 Subject: [ALSA] hda-intel - Disable INTX when MSI is used Call pci_intx() to disable/enable INTX when MSI is used/unused. Nvidia and AMD boards seem to have problems with MSI when INTX isn't disabled. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d15c9b845f23..e6a1e37b373a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1391,6 +1391,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) return -1; } chip->irq = chip->pci->irq; + pci_intx(chip->pci, !chip->msi); return 0; } -- cgit v1.2.3-59-g8ed1b From 9fb62c9f23d437241051e27a11de568361a4745d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 21 Nov 2006 19:01:51 +0100 Subject: [ALSA] korg1212: fix printk format warning sound/pci/korg1212/korg1212.c:2359: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/korg1212/korg1212.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index b4e98f36229b..21d0899ac382 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2356,7 +2356,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev * if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), dsp_code->size, &korg1212->dma_dsp) < 0) { - snd_printk(KERN_ERR "korg1212: can not allocate dsp code memory (%d bytes)\n", dsp_code->size); + snd_printk(KERN_ERR "korg1212: cannot allocate dsp code memory (%zd bytes)\n", dsp_code->size); snd_korg1212_free(korg1212); #ifdef FIRMWARE_IN_THE_KERNEL if (dsp_code != &static_dsp_code) -- cgit v1.2.3-59-g8ed1b From 56bb0cab1c1698544e61409e3727f2b6bc205501 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Nov 2006 11:52:52 +0100 Subject: [ALSA] hda-codec - Add asus-laptop model for ALC861 (ALC660) Added a new model 'asus-laptop' for ASUS F2*/F3* laptops with ALC861 (equivalent with ALC660) codec chip. Also fixed the model for PCI SSID 1043:1338. Corresponding to ALSA bug#2480. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 40 ++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index d853a303ffcb..15d102a94619 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -839,6 +839,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. uniwill-m31 Uniwill M31 laptop toshiba Toshiba laptop support asus Asus laptop support + asus-laptop ASUS F2/F3 laptops auto auto-config reading BIOS (default) CMI9880 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index aeb408cbe1ef..60f199459ad4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -101,6 +101,7 @@ enum { ALC861_UNIWILL_M31, ALC861_TOSHIBA, ALC861_ASUS, + ALC861_ASUS_LAPTOP, ALC861_AUTO, ALC861_MODEL_LAST, }; @@ -6901,9 +6902,17 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = { .private_value = ARRAY_SIZE(alc861_asus_modes), }, { } -}; +}; + +/* additional mixer */ +static snd_kcontrol_new_t alc861_asus_laptop_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT), + { } +}; - /* * generic initialization of ADC, input mixers and output mixers */ @@ -7153,6 +7162,12 @@ static struct hda_verb alc861_asus_init_verbs[] = { { } }; +/* additional init verbs for ASUS laptops */ +static struct hda_verb alc861_asus_laptop_init_verbs[] = { + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ + { } +}; /* * generic initialization of ADC, input mixers and output mixers @@ -7530,8 +7545,11 @@ static struct hda_board_config alc861_cfg_tbl[] = { { .modelname = "asus", .config = ALC861_ASUS}, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1393, .config = ALC861_ASUS }, + { .modelname = "asus-laptop", .config = ALC861_ASUS_LAPTOP }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x1335, + .config = ALC861_ASUS_LAPTOP }, /* ASUS F2/F3 */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x1338, - .config = ALC861_ASUS }, + .config = ALC861_ASUS_LAPTOP }, /* ASUS F2/F3 */ { .modelname = "auto", .config = ALC861_AUTO }, {} }; @@ -7626,7 +7644,21 @@ static struct alc_config_preset alc861_presets[] = { .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, }, -}; + [ALC861_ASUS_LAPTOP] = { + .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, + .init_verbs = { alc861_asus_init_verbs, + alc861_asus_laptop_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, +}; static int patch_alc861(struct hda_codec *codec) -- cgit v1.2.3-59-g8ed1b From 59d6e149d9e5c476138911c95f288ec3feb3a34d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 23 Nov 2006 18:37:00 +0100 Subject: [ALSA] Remove obsolete typedefs.h Removed obsolete typedefs.h. It existes only for backward compatibility, and now all codes should be free from such typedefs. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/core.h | 2 - include/sound/typedefs.h | 173 ----------------------------------------------- 2 files changed, 175 deletions(-) delete mode 100644 include/sound/typedefs.h diff --git a/include/sound/core.h b/include/sound/core.h index 83a575a29d0f..506aa9f1e20f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -427,6 +427,4 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) #endif #endif -#include "typedefs.h" - #endif /* __SOUND_CORE_H */ diff --git a/include/sound/typedefs.h b/include/sound/typedefs.h deleted file mode 100644 index f454b0206b93..000000000000 --- a/include/sound/typedefs.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Typedef's for backward compatibility (for out-of-kernel drivers) - * - * This file will be removed soon in future - */ - -/* core stuff */ -typedef struct snd_card snd_card_t; -typedef struct snd_device snd_device_t; -typedef struct snd_device_ops snd_device_ops_t; -typedef enum snd_card_type snd_card_type_t; -typedef struct snd_minor snd_minor_t; - -/* info */ -typedef struct snd_info_entry snd_info_entry_t; -typedef struct snd_info_buffer snd_info_buffer_t; - -/* control */ -typedef struct snd_ctl_file snd_ctl_file_t; -typedef struct snd_kcontrol snd_kcontrol_t; -typedef struct snd_kcontrol_new snd_kcontrol_new_t; -typedef struct snd_kcontrol_volatile snd_kcontrol_volatile_t; -typedef struct snd_kctl_event snd_kctl_event_t; -typedef struct snd_aes_iec958 snd_aes_iec958_t; -typedef struct snd_ctl_card_info snd_ctl_card_info_t; -typedef struct snd_ctl_elem_id snd_ctl_elem_id_t; -typedef struct snd_ctl_elem_list snd_ctl_elem_list_t; -typedef struct snd_ctl_elem_info snd_ctl_elem_info_t; -typedef struct snd_ctl_elem_value snd_ctl_elem_value_t; -typedef struct snd_ctl_event snd_ctl_event_t; -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) -typedef struct snd_mixer_oss snd_mixer_oss_t; -#endif - -/* timer */ -typedef struct snd_timer snd_timer_t; -typedef struct snd_timer_instance snd_timer_instance_t; -typedef struct snd_timer_id snd_timer_id_t; -typedef struct snd_timer_ginfo snd_timer_ginfo_t; -typedef struct snd_timer_gparams snd_timer_gparams_t; -typedef struct snd_timer_gstatus snd_timer_gstatus_t; -typedef struct snd_timer_select snd_timer_select_t; -typedef struct snd_timer_info snd_timer_info_t; -typedef struct snd_timer_params snd_timer_params_t; -typedef struct snd_timer_status snd_timer_status_t; -typedef struct snd_timer_read snd_timer_read_t; -typedef struct snd_timer_tread snd_timer_tread_t; - -/* PCM */ -typedef struct snd_pcm snd_pcm_t; -typedef struct snd_pcm_str snd_pcm_str_t; -typedef struct snd_pcm_substream snd_pcm_substream_t; -typedef struct snd_pcm_info snd_pcm_info_t; -typedef struct snd_pcm_hw_params snd_pcm_hw_params_t; -typedef struct snd_pcm_sw_params snd_pcm_sw_params_t; -typedef struct snd_pcm_channel_info snd_pcm_channel_info_t; -typedef struct snd_pcm_status snd_pcm_status_t; -typedef struct snd_pcm_mmap_status snd_pcm_mmap_status_t; -typedef struct snd_pcm_mmap_control snd_pcm_mmap_control_t; -typedef struct snd_mask snd_mask_t; -typedef struct snd_sg_buf snd_pcm_sgbuf_t; - -typedef struct snd_interval snd_interval_t; -typedef struct snd_xferi snd_xferi_t; -typedef struct snd_xfern snd_xfern_t; -typedef struct snd_xferv snd_xferv_t; - -typedef struct snd_pcm_file snd_pcm_file_t; -typedef struct snd_pcm_runtime snd_pcm_runtime_t; -typedef struct snd_pcm_hardware snd_pcm_hardware_t; -typedef struct snd_pcm_ops snd_pcm_ops_t; -typedef struct snd_pcm_hw_rule snd_pcm_hw_rule_t; -typedef struct snd_pcm_hw_constraints snd_pcm_hw_constraints_t; -typedef struct snd_ratnum ratnum_t; -typedef struct snd_ratden ratden_t; -typedef struct snd_pcm_hw_constraint_ratnums snd_pcm_hw_constraint_ratnums_t; -typedef struct snd_pcm_hw_constraint_ratdens snd_pcm_hw_constraint_ratdens_t; -typedef struct snd_pcm_hw_constraint_list snd_pcm_hw_constraint_list_t; -typedef struct snd_pcm_group snd_pcm_group_t; -typedef struct snd_pcm_notify snd_pcm_notify_t; - -/* rawmidi */ -typedef struct snd_rawmidi snd_rawmidi_t; -typedef struct snd_rawmidi_info snd_rawmidi_info_t; -typedef struct snd_rawmidi_params snd_rawmidi_params_t; -typedef struct snd_rawmidi_status snd_rawmidi_status_t; -typedef struct snd_rawmidi_runtime snd_rawmidi_runtime_t; -typedef struct snd_rawmidi_substream snd_rawmidi_substream_t; -typedef struct snd_rawmidi_str snd_rawmidi_str_t; -typedef struct snd_rawmidi_ops snd_rawmidi_ops_t; -typedef struct snd_rawmidi_global_ops snd_rawmidi_global_ops_t; -typedef struct snd_rawmidi_file snd_rawmidi_file_t; - -/* hwdep */ -typedef struct snd_hwdep snd_hwdep_t; -typedef struct snd_hwdep_info snd_hwdep_info_t; -typedef struct snd_hwdep_dsp_status snd_hwdep_dsp_status_t; -typedef struct snd_hwdep_dsp_image snd_hwdep_dsp_image_t; -typedef struct snd_hwdep_ops snd_hwdep_ops_t; - -/* sequencer */ -typedef struct snd_seq_port_info snd_seq_port_info_t; -typedef struct snd_seq_port_subscribe snd_seq_port_subscribe_t; -typedef struct snd_seq_event snd_seq_event_t; -typedef struct snd_seq_addr snd_seq_addr_t; -typedef struct snd_seq_ev_volume snd_seq_ev_volume_t; -typedef struct snd_seq_ev_loop snd_seq_ev_loop_t; -typedef struct snd_seq_remove_events snd_seq_remove_events_t; -typedef struct snd_seq_query_subs snd_seq_query_subs_t; -typedef struct snd_seq_system_info snd_seq_system_info_t; -typedef struct snd_seq_client_info snd_seq_client_info_t; -typedef struct snd_seq_queue_info snd_seq_queue_info_t; -typedef struct snd_seq_queue_status snd_seq_queue_status_t; -typedef struct snd_seq_queue_tempo snd_seq_queue_tempo_t; -typedef struct snd_seq_queue_owner snd_seq_queue_owner_t; -typedef struct snd_seq_queue_timer snd_seq_queue_timer_t; -typedef struct snd_seq_queue_client snd_seq_queue_client_t; -typedef struct snd_seq_client_pool snd_seq_client_pool_t; -typedef struct snd_seq_instr snd_seq_instr_t; -typedef struct snd_seq_instr_data snd_seq_instr_data_t; -typedef struct snd_seq_instr_header snd_seq_instr_header_t; - -typedef struct snd_seq_user_client user_client_t; -typedef struct snd_seq_kernel_client kernel_client_t; -typedef struct snd_seq_client client_t; -typedef struct snd_seq_queue queue_t; - -/* seq_device */ -typedef struct snd_seq_device snd_seq_device_t; -typedef struct snd_seq_dev_ops snd_seq_dev_ops_t; - -/* seq_midi */ -typedef struct snd_midi_event snd_midi_event_t; - -/* seq_midi_emul */ -typedef struct snd_midi_channel snd_midi_channel_t; -typedef struct snd_midi_channel_set snd_midi_channel_set_t; -typedef struct snd_midi_op snd_midi_op_t; - -/* seq_oss */ -typedef struct snd_seq_oss_arg snd_seq_oss_arg_t; -typedef struct snd_seq_oss_callback snd_seq_oss_callback_t; -typedef struct snd_seq_oss_reg snd_seq_oss_reg_t; - -/* virmidi */ -typedef struct snd_virmidi_dev snd_virmidi_dev_t; -typedef struct snd_virmidi snd_virmidi_t; - -/* seq_instr */ -typedef struct snd_seq_kcluster snd_seq_kcluster_t; -typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t; -typedef struct snd_seq_kinstr snd_seq_kinstr_t; -typedef struct snd_seq_kinstr_list snd_seq_kinstr_list_t; - -/* ac97 */ -typedef struct snd_ac97_bus ac97_bus_t; -typedef struct snd_ac97_bus_ops ac97_bus_ops_t; -typedef struct snd_ac97_template ac97_template_t; -typedef struct snd_ac97 ac97_t; - -/* opl3/4 */ -typedef struct snd_opl3 opl3_t; -typedef struct snd_opl4 opl4_t; - -/* mpu401 */ -typedef struct snd_mpu401 mpu401_t; - -/* i2c */ -typedef struct snd_i2c_device snd_i2c_device_t; -typedef struct snd_i2c_bus snd_i2c_bus_t; - -typedef struct snd_ak4531 ak4531_t; - -- cgit v1.2.3-59-g8ed1b From d1d985f019c3b290e09881b7b23abdc87aee2895 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 23 Nov 2006 19:27:12 +0100 Subject: [ALSA] Fix obsolete *_t typedefs Fixed obsolete *_t typedefs. Now completely removed. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/als300.c | 6 +++--- sound/pci/hda/patch_realtek.c | 6 +++--- sound/pci/hda/patch_sigmatel.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 9f406fbe0d95..8afcb98ca7bb 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -444,7 +444,7 @@ static int snd_als300_capture_close(struct snd_pcm_substream *substream) } static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream, - snd_pcm_hw_params_t * hw_params) + struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); @@ -673,7 +673,7 @@ static void snd_als300_init(struct snd_als300 *chip) snd_als300_dbgcallleave(); } -static int __devinit snd_als300_create(snd_card_t *card, +static int __devinit snd_als300_create(struct snd_card *card, struct pci_dev *pci, int chip_type, struct snd_als300 **rchip) { @@ -681,7 +681,7 @@ static int __devinit snd_als300_create(snd_card_t *card, void *irq_handler; int err; - static snd_device_ops_t ops = { + static struct snd_device_ops ops = { .dev_free = snd_als300_dev_free, }; *rchip = NULL; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 60f199459ad4..02c465147d98 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5115,7 +5115,7 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { { } /* end */ }; -static snd_kcontrol_new_t alc883_fivestack_mixer[] = { +static struct snd_kcontrol_new alc883_fivestack_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), @@ -6799,7 +6799,7 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = { { } /* end */ }; -static snd_kcontrol_new_t alc861_toshiba_mixer[] = { +static struct snd_kcontrol_new alc861_toshiba_mixer[] = { /* output mixer control */ HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), @@ -6905,7 +6905,7 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = { }; /* additional mixer */ -static snd_kcontrol_new_t alc861_asus_laptop_mixer[] = { +static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT), diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1b428a1eafd0..c8696ddc03ac 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -301,7 +301,7 @@ static struct snd_kcontrol_new stac9227_mixer[] = { { } /* end */ }; -static snd_kcontrol_new_t stac927x_mixer[] = { +static struct snd_kcontrol_new stac927x_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -316,7 +316,7 @@ static snd_kcontrol_new_t stac927x_mixer[] = { { } /* end */ }; -static snd_kcontrol_new_t stac9205_mixer[] = { +static struct snd_kcontrol_new stac9205_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Input Source", -- cgit v1.2.3-59-g8ed1b From 048b945077bdc7e8dff5d5810ff2a0ced3590ca9 Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Fri, 24 Nov 2006 13:03:58 +0100 Subject: [ALSA] echoaudio, add TLV support This patch adds TLV support to the echoaudio driver. All gains are in the range -127dB to +6dB with steps of 1dB, and -128 is mute. VU-meters levels go from -128 to 0dB. The input gain of the Layla20 ranges from -25dB to +25dB in steps of 0.5dB. Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/echoaudio/darla20.c | 1 + sound/pci/echoaudio/darla24.c | 1 + sound/pci/echoaudio/echo3g.c | 1 + sound/pci/echoaudio/echoaudio.c | 18 +++++++++++++++++- sound/pci/echoaudio/gina20.c | 1 + sound/pci/echoaudio/gina24.c | 1 + sound/pci/echoaudio/indigo.c | 1 + sound/pci/echoaudio/indigodj.c | 1 + sound/pci/echoaudio/indigoio.c | 1 + sound/pci/echoaudio/layla20.c | 1 + sound/pci/echoaudio/layla24.c | 1 + sound/pci/echoaudio/mia.c | 1 + sound/pci/echoaudio/mona.c | 1 + 13 files changed, 29 insertions(+), 1 deletion(-) diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index b7108e29a668..8e7fe033270f 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index e59a982ee361..a13c623eb999 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index 12099fe1547d..8fb15823aca5 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 047e0b5bf15d..3410bd4450ad 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -34,6 +34,7 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; +static DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1); static int get_firmware(const struct firmware **fw_entry, const struct firmware *frm, struct echoaudio *chip) @@ -1011,17 +1012,21 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = { .name = "Line Playback Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_output_gain_info, .get = snd_echo_output_gain_get, .put = snd_echo_output_gain_put, + .tlv = {.p = db_scale_output_gain}, }; #else static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = { .name = "PCM Playback Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_output_gain_info, .get = snd_echo_output_gain_get, .put = snd_echo_output_gain_put, + .tlv = {.p = db_scale_output_gain}, }; #endif @@ -1080,12 +1085,16 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol, return changed; } +static DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0); + static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = { .name = "Line Capture Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_input_gain_info, .get = snd_echo_input_gain_get, .put = snd_echo_input_gain_put, + .tlv = {.p = db_scale_input_gain}, }; #endif /* ECHOCARD_HAS_INPUT_GAIN */ @@ -1277,9 +1286,11 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = { .name = "Monitor Mixer Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_mixer_info, .get = snd_echo_mixer_get, .put = snd_echo_mixer_put, + .tlv = {.p = db_scale_output_gain}, }; #endif /* ECHOCARD_HAS_MONITOR */ @@ -1343,9 +1354,11 @@ static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = { .name = "VMixer Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_vmixer_info, .get = snd_echo_vmixer_get, .put = snd_echo_vmixer_put, + .tlv = {.p = db_scale_output_gain}, }; #endif /* ECHOCARD_HAS_VMIXER */ @@ -1753,9 +1766,12 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = { .name = "VU-meters", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_vumeters_info, .get = snd_echo_vumeters_get, + .tlv = {.p = db_scale_output_gain}, }; diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index 29d6d12f80ca..af4d32026e4a 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index e464d720d0bd..9ff454a947ed 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index bfd2467099ac..37eb726fd03d 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 8ed7ff1fd875..dc8b91824181 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index a8788e959171..eadf3263453a 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index e503d74b3ba9..6cede497579e 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index d4581fdc841c..44f735426aa0 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index be40c64263d2..dc172d03ac3f 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 5dc512add372..c856ed50dd9a 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From d9ea472c743ccd7344055cb118bc210befbd8007 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:34:06 +0100 Subject: [ALSA] Add PCI quirk list helper function Added a helper function snd_pci_quirk_lookup() to look up PCI SSID quirk list. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/core.h | 25 +++++++++++++++++++++++++ sound/core/misc.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/sound/core.h b/include/sound/core.h index 506aa9f1e20f..3c493ad620d1 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -427,4 +427,29 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) #endif #endif +/* PCI quirk list helper */ +struct snd_pci_quirk { + unsigned short subvendor; /* PCI subvendor ID */ + unsigned short subdevice; /* PCI subdevice ID */ + int value; /* value */ +#ifdef CONFIG_SND_DEBUG_DETECT + const char *name; /* name of the device (optional) */ +#endif +}; + +#define _SND_PCI_QUIRK_ID(vend,dev) \ + .subvendor = (vend), .subdevice = (dev) +#define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)} +#ifdef CONFIG_SND_DEBUG_DETECT +#define SND_PCI_QUIRK(vend,dev,xname,val) \ + {_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)} +#else +#define SND_PCI_QUIRK(vend,dev,xname,val) \ + {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)} +#endif + +const struct snd_pci_quirk * +snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list); + + #endif /* __SOUND_CORE_H */ diff --git a/sound/core/misc.c b/sound/core/misc.c index 03fc711f4127..6db86a7c9704 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -78,3 +78,31 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) EXPORT_SYMBOL(snd_verbose_printd); #endif + +#ifdef CONFIG_PCI +#include +/** + * snd_pci_quirk_lookup - look up a PCI SSID quirk list + * @pci: pci_dev handle + * @list: quirk list, terminated by a null entry + * + * Look through the given quirk list and finds a matching entry + * with the same PCI SSID. When subdevice is 0, all subdevice + * values may match. + * + * Returns the matched entry pointer, or NULL if nothing matched. + */ +const struct snd_pci_quirk * +snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) +{ + const struct snd_pci_quirk *q; + + for (q = list; q->subvendor; q++) + if (q->subvendor == pci->subsystem_vendor && + (!q->subdevice || q->subdevice == pci->subsystem_device)) + return q; + return NULL; +} + +EXPORT_SYMBOL(snd_pci_quirk_lookup); +#endif -- cgit v1.2.3-59-g8ed1b From f41bea84c030793b502aa2526bb22476788e731e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:35:18 +0100 Subject: [ALSA] atiixp - Use quirk list helper function Clean up ac97_codec quirk using snd_pci_quirk_lookup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/atiixp.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 92df811d695d..7d8053b5e8d5 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -296,21 +296,9 @@ static struct pci_device_id snd_atiixp_ids[] = { MODULE_DEVICE_TABLE(pci, snd_atiixp_ids); -struct atiixp_quirk { - unsigned short subvendor; - unsigned short subdevice; - const char *name; - int ac97_codec; -}; - -static struct atiixp_quirk atiixp_quirks[] __devinitdata = { - { - .subvendor = 0x15bd, - .subdevice = 0x3100, - .name = "DFI RS482", - .ac97_codec = 0, - }, - { .subvendor = 0 } /* terminator */ +static struct snd_pci_quirk atiixp_quirks[] __devinitdata = { + SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0), + { } /* terminator */ }; /* @@ -574,17 +562,13 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) static int ac97_probing_bugs(struct pci_dev *pci) { - int i = 0; - - while (atiixp_quirks[i].subvendor) { - if (pci->subsystem_vendor == atiixp_quirks[i].subvendor && - pci->subsystem_device == atiixp_quirks[i].subdevice) { - printk(KERN_INFO "Atiixp quirk for %s. " - "Forcing codec %d\n", atiixp_quirks[i].name, - atiixp_quirks[i].ac97_codec); - return atiixp_quirks[i].ac97_codec; - } - i++; + const struct snd_pci_quirk *q; + + q = snd_pci_quirk_lookup(pci, atiixp_quirks); + if (q) { + snd_printdd(KERN_INFO "Atiixp quirk for %s. " + "Forcing codec %d\n", q->name, q->value); + return q->value; } /* this hardware doesn't need workarounds. Probe for codec */ return -1; -- cgit v1.2.3-59-g8ed1b From e2b6d13be4ac3b564ac642a76756f6cf1a7b7b99 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:36:13 +0100 Subject: [ALSA] nm256 - Use quirk list helper function Clean up nm256-quirk lookup using snd_pci_quirk_lookup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/nm256/nm256.c | 56 +++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 879e31a9f9c6..03b3a4792f73 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1628,23 +1628,15 @@ __error: } -struct nm256_quirk { - unsigned short vendor; - unsigned short device; - int type; -}; - enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 }; -static struct nm256_quirk nm256_quirks[] __devinitdata = { +static struct snd_pci_quirk nm256_quirks[] __devinitdata = { /* HP omnibook 4150 has cs4232 codec internally */ - { .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED }, - /* Sony PCG-F305 */ - { .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND }, - /* Dell Latitude LS */ - { .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND }, - /* Dell Latitude CSx */ - { .vendor = 0x1028, .device = 0x0091, .type = NM_RESET_WORKAROUND_2 }, + SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_BLACKLISTED), + /* Reset workarounds to avoid lock-ups */ + SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND), + SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND), + SND_PCI_QUIRK(0x1028, 0x0091, "Dell Latitude CSx", NM_RESET_WORKAROUND_2), { } /* terminator */ }; @@ -1655,26 +1647,22 @@ static int __devinit snd_nm256_probe(struct pci_dev *pci, struct snd_card *card; struct nm256 *chip; int err; - struct nm256_quirk *q; - u16 subsystem_vendor, subsystem_device; - - pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); - - for (q = nm256_quirks; q->vendor; q++) { - if (q->vendor == subsystem_vendor && q->device == subsystem_device) { - switch (q->type) { - case NM_BLACKLISTED: - printk(KERN_INFO "nm256: The device is blacklisted. " - "Loading stopped\n"); - return -ENODEV; - case NM_RESET_WORKAROUND_2: - reset_workaround_2 = 1; - /* Fall-through */ - case NM_RESET_WORKAROUND: - reset_workaround = 1; - break; - } + const struct snd_pci_quirk *q; + + q = snd_pci_quirk_lookup(pci, nm256_quirks); + if (q) { + snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name); + switch (q->value) { + case NM_BLACKLISTED: + printk(KERN_INFO "nm256: The device is blacklisted. " + "Loading stopped\n"); + return -ENODEV; + case NM_RESET_WORKAROUND_2: + reset_workaround_2 = 1; + /* Fall-through */ + case NM_RESET_WORKAROUND: + reset_workaround = 1; + break; } } -- cgit v1.2.3-59-g8ed1b From 1061eeb44493176eb1d12b47d619e61c428c4395 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:36:46 +0100 Subject: [ALSA] maestro3 - Use quirk list helper function Clean up maestro3 amp and GPIO quirks using snd_pci_quirk_lookup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/maestro3.c | 294 ++++++++++++++++++++++----------------------------- 1 file changed, 127 insertions(+), 167 deletions(-) diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 053ea4fdbffd..4526904e3f86 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -770,21 +770,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)"); /* */ -/* quirk lists */ -struct m3_quirk { - const char *name; /* device name */ - u16 vendor, device; /* subsystem ids */ - int amp_gpio; /* gpio pin # for external amp, -1 = default */ - int irda_workaround; /* non-zero if avoid to touch 0x10 on GPIO_DIRECTION - (e.g. for IrDA on Dell Inspirons) */ -}; - -struct m3_hv_quirk { - u16 vendor, device, subsystem_vendor, subsystem_device; - u32 config; /* ALLEGRO_CONFIG hardware volume bits */ - int is_omnibook; /* Do HP OmniBook GPIO magic? */ -}; - struct m3_list { int curlen; int mem_addr; @@ -832,8 +817,6 @@ struct snd_m3 { struct snd_pcm *pcm; struct pci_dev *pci; - const struct m3_quirk *quirk; - const struct m3_hv_quirk *hv_quirk; int dacs_active; int timer_users; @@ -847,7 +830,11 @@ struct snd_m3 { u8 reset_state; int external_amp; - int amp_gpio; + int amp_gpio; /* gpio pin # for external amp, -1 = default */ + unsigned int hv_config; /* hardware-volume config bits */ + unsigned irda_workaround :1; /* avoid to touch 0x10 on GPIO_DIRECTION + (e.g. for IrDA on Dell Inspirons) */ + unsigned is_omnibook :1; /* Do HP OmniBook GPIO magic? */ /* midi */ struct snd_rawmidi *rmidi; @@ -896,127 +883,104 @@ static struct pci_device_id snd_m3_ids[] = { MODULE_DEVICE_TABLE(pci, snd_m3_ids); -static const struct m3_quirk m3_quirk_list[] = { - /* panasonic CF-28 "toughbook" */ - { - .name = "Panasonic CF-28", - .vendor = 0x10f7, - .device = 0x833e, - .amp_gpio = 0x0d, - }, - /* panasonic CF-72 "toughbook" */ - { - .name = "Panasonic CF-72", - .vendor = 0x10f7, - .device = 0x833d, - .amp_gpio = 0x0d, - }, - /* Dell Inspiron 4000 */ - { - .name = "Dell Inspiron 4000", - .vendor = 0x1028, - .device = 0x00b0, - .amp_gpio = -1, - .irda_workaround = 1, - }, - /* Dell Inspiron 8000 */ - { - .name = "Dell Inspiron 8000", - .vendor = 0x1028, - .device = 0x00a4, - .amp_gpio = -1, - .irda_workaround = 1, - }, - /* Dell Inspiron 8100 */ - { - .name = "Dell Inspiron 8100", - .vendor = 0x1028, - .device = 0x00e6, - .amp_gpio = -1, - .irda_workaround = 1, - }, - /* NEC LM800J/7 */ - { - .name = "NEC LM800J/7", - .vendor = 0x1033, - .device = 0x80f1, - .amp_gpio = 0x03, - }, - /* LEGEND ZhaoYang 3100CF */ - { - .name = "LEGEND ZhaoYang 3100CF", - .vendor = 0x1509, - .device = 0x1740, - .amp_gpio = 0x03, - }, - /* END */ - { NULL } +static struct snd_pci_quirk m3_amp_quirk_list[] __devinitdata = { + SND_PCI_QUIRK(0x10f7, 0x833e, "Panasonic CF-28", 0x0d), + SND_PCI_QUIRK(0x10f7, 0x833d, "Panasonic CF-72", 0x0d), + SND_PCI_QUIRK(0x1033, 0x80f1, "NEC LM800J/7", 0x03), + SND_PCI_QUIRK(0x1509, 0x1740, "LEGEND ZhaoYang 3100CF", 0x03), + { } /* END */ +}; + +static struct snd_pci_quirk m3_irda_quirk_list[] __devinitdata = { + SND_PCI_QUIRK(0x1028, 0x00b0, "Dell Inspiron 4000", 1), + SND_PCI_QUIRK(0x1028, 0x00a4, "Dell Inspiron 8000", 1), + SND_PCI_QUIRK(0x1028, 0x00e6, "Dell Inspiron 8100", 1), + { } /* END */ }; -/* These values came from the Windows driver. */ -static const struct m3_hv_quirk m3_hv_quirk_list[] = { +/* hardware volume quirks */ +static struct snd_pci_quirk m3_hv_quirk_list[] __devinitdata = { /* Allegro chips */ - { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 }, - { 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */ - { 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 }, + SND_PCI_QUIRK(0x0E11, 0x002E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x0E11, 0x0094, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x0E11, 0xB112, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x0E11, 0xB114, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x103C, 0x0012, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x103C, 0x0018, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x103C, 0x001C, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x103C, 0x001D, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x103C, 0x001E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x107B, 0x3350, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x10F7, 0x8338, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x10F7, 0x833C, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x10F7, 0x833D, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x10F7, 0x833E, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x10F7, 0x833F, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x13BD, 0x1018, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x13BD, 0x1019, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x13BD, 0x101A, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x14FF, 0x0F03, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x14FF, 0x0F04, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x14FF, 0x0F05, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x156D, 0xB400, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x156D, 0xB795, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x156D, 0xB797, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x156D, 0xC700, NULL, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD), + SND_PCI_QUIRK(0x1033, 0x80F1, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x103C, 0x001A, NULL, /* HP OmniBook 6100 */ + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x107B, 0x340A, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x107B, 0x3450, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x109F, 0x3134, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x109F, 0x3161, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0x3280, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0x3281, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0xC002, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0xC003, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x1509, 0x1740, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x1610, 0x0010, NULL, + HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x1042, 0x1042, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x107B, 0x9500, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x14FF, 0x0F06, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x1558, 0x8586, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x161F, 0x2011, NULL, HV_CTRL_ENABLE), /* Maestro3 chips */ - { 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */ - { 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */ - { 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 }, - { 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, - { 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 }, - { 0 } + SND_PCI_QUIRK(0x103C, 0x000E, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x103C, 0x0010, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x103C, 0x0011, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x103C, 0x001B, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x104D, 0x80A6, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x104D, 0x80AA, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x107B, 0x5300, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x110A, 0x1998, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x13BD, 0x1015, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x13BD, 0x101C, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x13BD, 0x1802, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x1599, 0x0715, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x5643, 0x5643, NULL, HV_CTRL_ENABLE), + SND_PCI_QUIRK(0x144D, 0x3260, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0x3261, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0xC000, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE), + SND_PCI_QUIRK(0x144D, 0xC001, NULL, HV_CTRL_ENABLE | REDUCED_DEBOUNCE), + { } /* END */ +}; + +/* HP Omnibook quirks */ +static struct snd_pci_quirk m3_omnibook_quirk_list[] __devinitdata = { + SND_PCI_QUIRK_ID(0x103c, 0x0010), /* HP OmniBook 6000 */ + SND_PCI_QUIRK_ID(0x103c, 0x0011), /* HP OmniBook 500 */ + { } /* END */ }; /* @@ -2055,7 +2019,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip) for (i = 0; i < 5; i++) { dir = inw(io + GPIO_DIRECTION); - if (! chip->quirk || ! chip->quirk->irda_workaround) + if (!chip->irda_workaround) dir |= 0x10; /* assuming pci bus master? */ snd_m3_remote_codec_config(io, 0); @@ -2478,7 +2442,7 @@ snd_m3_chip_init(struct snd_m3 *chip) DISABLE_LEGACY); pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w); - if (chip->hv_quirk && chip->hv_quirk->is_omnibook) { + if (chip->is_omnibook) { /* * Volume buttons on some HP OmniBook laptops don't work * correctly. This makes them work for the most part. @@ -2495,8 +2459,7 @@ snd_m3_chip_init(struct snd_m3 *chip) } pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD); - if (chip->hv_quirk) - n |= chip->hv_quirk->config; + n |= chip->hv_config; /* For some reason we must always use reduced debounce. */ n |= REDUCED_DEBOUNCE; n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; @@ -2544,7 +2507,7 @@ snd_m3_enable_ints(struct snd_m3 *chip) /* TODO: MPU401 not supported yet */ val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/; - if (chip->hv_quirk && (chip->hv_quirk->config & HV_CTRL_ENABLE)) + if (chip->hv_config & HV_CTRL_ENABLE) val |= HV_INT_ENABLE; outw(val, io + HOST_INT_CTRL); outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, @@ -2708,8 +2671,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, { struct snd_m3 *chip; int i, err; - const struct m3_quirk *quirk; - const struct m3_hv_quirk *hv_quirk; + const struct snd_pci_quirk *quirk; static struct snd_device_ops ops = { .dev_free = snd_m3_dev_free, }; @@ -2749,34 +2711,32 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, chip->pci = pci; chip->irq = -1; - for (quirk = m3_quirk_list; quirk->vendor; quirk++) { - if (pci->subsystem_vendor == quirk->vendor && - pci->subsystem_device == quirk->device) { - printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name); - chip->quirk = quirk; - break; - } - } - - for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) { - if (pci->vendor == hv_quirk->vendor && - pci->device == hv_quirk->device && - pci->subsystem_vendor == hv_quirk->subsystem_vendor && - pci->subsystem_device == hv_quirk->subsystem_device) { - chip->hv_quirk = hv_quirk; - break; - } - } - chip->external_amp = enable_amp; if (amp_gpio >= 0 && amp_gpio <= 0x0f) chip->amp_gpio = amp_gpio; - else if (chip->quirk && chip->quirk->amp_gpio >= 0) - chip->amp_gpio = chip->quirk->amp_gpio; - else if (chip->allegro_flag) - chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; - else /* presumably this is for all 'maestro3's.. */ - chip->amp_gpio = GPO_EXT_AMP_M3; + else { + quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list); + if (quirk) { + snd_printdd(KERN_INFO "maestro3: set amp-gpio " + "for '%s'\n", quirk->name); + chip->amp_gpio = quirk->value; + } else if (chip->allegro_flag) + chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; + else /* presumably this is for all 'maestro3's.. */ + chip->amp_gpio = GPO_EXT_AMP_M3; + } + + quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list); + if (quirk) { + snd_printdd(KERN_INFO "maestro3: enabled irda workaround " + "for '%s'\n", quirk->name); + chip->irda_workaround = 1; + } + quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list); + if (quirk) + chip->hv_config = quirk->value; + if (snd_pci_quirk_lookup(pci, m3_omnibook_quirk_list)) + chip->is_omnibook = 1; chip->num_substreams = NR_DSPS; chip->substreams = kcalloc(chip->num_substreams, sizeof(struct m3_dma), -- cgit v1.2.3-59-g8ed1b From 9d74958a845b54c8ccfd4c6d14659f601e6ef43b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:37:18 +0100 Subject: [ALSA] via82xx - Use quirk list helper function Clean up dxs_support quirk list using snd_pci_quirk_lookup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/via82xx.c | 130 +++++++++++++++++++--------------------------------- 1 file changed, 48 insertions(+), 82 deletions(-) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 0440df7de37d..22caf5d7ff1e 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2357,93 +2357,59 @@ static struct via823x_info via823x_cards[] __devinitdata = { /* * auto detection of DXS channel supports. */ -struct dxs_whitelist { - unsigned short subvendor; - unsigned short subdevice; - unsigned short mask; - short action; /* new dxs_support value */ + +static struct snd_pci_quirk dxs_whitelist[] __devinitdata = { + SND_PCI_QUIRK(0x1005, 0x4710, "Avance Logic Mobo", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1019, 0x0996, "ESC Mobo", VIA_DXS_48K), + SND_PCI_QUIRK(0x1019, 0x0a81, "ECS K7VTA3 v8.0", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1019, 0x0a85, "ECS L7VMM2", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1019, 0, "ESC K8", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1019, 0xaa01, "ESC K8T890-A", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1025, 0x0033, "Acer Inspire 1353LM", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1025, 0x0046, "Acer Aspire 1524 WLMi", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1043, 0, "ASUS A7/A8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1071, 0, "Diverse Notebook", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x7142, "MSI K8MM-V", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0, "MSI Mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x147b, 0x1401, "ABIT KD7(-RAID)", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1411, "ABIT VA-20", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1413, "ABIT KV8 Pro", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1415, "ABIT AV8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x14ff, 0x0403, "Twinhead mobo", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x14ff, 0x0408, "Twinhead laptop", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1558, 0x4701, "Clevo D470", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1584, 0x8120, "Diverse Laptop", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1584, 0x8123, "Targa/Uniwill", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x161f, 0x202b, "Amira Notebook", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x161f, 0x2032, "m680x machines", VIA_DXS_48K), + SND_PCI_QUIRK(0x1631, 0xe004, "PB EasyNote 3174", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1695, 0x3005, "EPoX EP-8K9A", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1695, 0, "EPoX mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x16f3, 0, "Jetway K8", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1734, 0, "FSC Laptop", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1849, 0x3059, "ASRock K7VM2", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1849, 0, "ASRock mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1919, 0x200a, "Soltek SL-K8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x4005, 0x4710, "MSI K7T266", VIA_DXS_SRC), + { } /* terminator */ }; static int __devinit check_dxs_list(struct pci_dev *pci, int revision) { - static struct dxs_whitelist whitelist[] __devinitdata = { - { .subvendor = 0x1005, .subdevice = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */ - { .subvendor = 0x1019, .subdevice = 0x0996, .action = VIA_DXS_48K }, - { .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */ - { .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */ - { .subvendor = 0x1019, .subdevice = 0xa101, .action = VIA_DXS_SRC }, - { .subvendor = 0x1019, .subdevice = 0xaa01, .action = VIA_DXS_SRC }, /* ECS K8T890-A */ - { .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */ - { .subvendor = 0x1025, .subdevice = 0x0046, .action = VIA_DXS_SRC }, /* Acer Aspire 1524 WLMi */ - { .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/ - { .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */ - { .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ - { .subvendor = 0x1043, .subdevice = 0x810d, .action = VIA_DXS_SRC }, /* ASUS */ - { .subvendor = 0x1043, .subdevice = 0x812a, .action = VIA_DXS_SRC }, /* ASUS A8V Deluxe */ - { .subvendor = 0x1043, .subdevice = 0x8174, .action = VIA_DXS_SRC }, /* ASUS */ - { .subvendor = 0x1043, .subdevice = 0x81b9, .action = VIA_DXS_SRC }, /* ASUS A8V-MX */ - { .subvendor = 0x1071, .subdevice = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */ - { .subvendor = 0x1071, .subdevice = 0x8399, .action = VIA_DXS_NO_VRA }, /* Umax AB 595T (VIA K8N800A - VT8237) */ - { .subvendor = 0x10cf, .subdevice = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ - { .subvendor = 0x1106, .subdevice = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ - { .subvendor = 0x1106, .subdevice = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */ - { .subvendor = 0x1106, .subdevice = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ - { .subvendor = 0x1106, .subdevice = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */ - { .subvendor = 0x1297, .subdevice = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */ - { .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ - { .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ - { .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */ - { .subvendor = 0x1462, .subdevice = 0x0430, .action = VIA_DXS_SRC }, /* MSI 7142 (K8MM-V) */ - { .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */ - { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ - { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ - { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */ - { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ - { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */ - { .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */ - { .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ - { .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */ - { .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */ - { .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */ - { .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ - { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_SRC }, /* Twinhead laptop */ - { .subvendor = 0x1558, .subdevice = 0x4701, .action = VIA_DXS_SRC }, /* Clevo D470 */ - { .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ - { .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */ - { .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */ - { .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */ - { .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ - { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ - { .subvendor = 0x1695, .subdevice = 0x300c, .action = VIA_DXS_SRC }, /* EPoX EP-8KRAI */ - { .subvendor = 0x1695, .subdevice = 0x300e, .action = VIA_DXS_SRC }, /* EPoX 9HEAI */ - { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */ - { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */ - { .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */ - { .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */ - { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ - { .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ - { .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */ - { .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */ - { .subvendor = 0x4005, .subdevice = 0x4710, .action = VIA_DXS_SRC }, /* MSI K7T266 Pro2 (MS-6380 V2.0) BIOS 3.7 */ - { } /* terminator */ - }; - const struct dxs_whitelist *w; - unsigned short subsystem_vendor; - unsigned short subsystem_device; - - pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); + const struct snd_pci_quirk *w; - for (w = whitelist; w->subvendor; w++) { - if (w->subvendor != subsystem_vendor) - continue; - if (w->mask) { - if ((w->mask & subsystem_device) == w->subdevice) - return w->action; - } else { - if (subsystem_device == w->subdevice) - return w->action; - } + w = snd_pci_quirk_lookup(pci, dxs_whitelist); + if (w) { + snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n", + w->name); + return w->value; } /* for newer revision, default to DXS_SRC */ -- cgit v1.2.3-59-g8ed1b From 5da8fa2516388a20a43cd928fda19f6ac2521afc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:38:18 +0100 Subject: [ALSA] ens1371 - Clean up quirks Clean up quirks in snd-ens1371 driver using snd_pci_quirk_lookup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ens1370.c | 154 ++++++++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 84 deletions(-) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index a84f6b21024f..425b167522d5 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -413,8 +413,6 @@ struct ensoniq { } u; struct pci_dev *pci; - unsigned short subsystem_vendor_id; - unsigned short subsystem_device_id; struct snd_card *card; struct snd_pcm *pcm1; /* DAC1/ADC PCM */ struct snd_pcm *pcm2; /* DAC2 PCM */ @@ -1607,11 +1605,26 @@ static void snd_ensoniq_mixer_free_ac97(struct snd_ac97 *ac97) ensoniq->u.es1371.ac97 = NULL; } -static struct { +struct es1371_quirk { unsigned short vid; /* vendor ID */ unsigned short did; /* device ID */ unsigned char rev; /* revision */ -} es1371_spdif_present[] __devinitdata = { +}; + +static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq, + struct es1371_quirk *list) +{ + while (list->vid != (unsigned short)PCI_ANY_ID) { + if (ensoniq->pci->vendor == list->vid && + ensoniq->pci->device == list->did && + ensoniq->rev == list->rev) + return 1; + list++; + } + return 0; +} + +static struct es1371_quirk es1371_spdif_present[] __devinitdata = { { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, @@ -1620,12 +1633,19 @@ static struct { { .vid = PCI_ANY_ID, .did = PCI_ANY_ID } }; -static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int has_line) +static struct snd_pci_quirk ens1373_line_quirk[] __devinitdata = { + SND_PCI_QUIRK_ID(0x1274, 0x2000), /* GA-7DXR */ + SND_PCI_QUIRK_ID(0x1458, 0xa000), /* GA-8IEXP */ + { } /* end */ +}; + +static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq, + int has_spdif, int has_line) { struct snd_card *card = ensoniq->card; struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; - int err, idx; + int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1371_codec_write, .read = snd_es1371_codec_read, @@ -1641,33 +1661,28 @@ static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int h ac97.scaps = AC97_SCAP_AUDIO; if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0) return err; - for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++) - if ((ensoniq->pci->vendor == es1371_spdif_present[idx].vid && - ensoniq->pci->device == es1371_spdif_present[idx].did && - ensoniq->rev == es1371_spdif_present[idx].rev) || has_spdif > 0) { - struct snd_kcontrol *kctl; - int i, index = 0; - - if (has_spdif < 0) - break; - - ensoniq->spdif_default = ensoniq->spdif_stream = - SNDRV_PCM_DEFAULT_CON_SPDIF; - outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); - - if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) - index++; - - for (i = 0; i < (int)ARRAY_SIZE(snd_es1371_mixer_spdif); i++) { - kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq); - if (! kctl) - return -ENOMEM; - kctl->id.index = index; - if ((err = snd_ctl_add(card, kctl)) < 0) - return err; - } - break; + if (has_spdif > 0 || + (!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) { + struct snd_kcontrol *kctl; + int i, index = 0; + + ensoniq->spdif_default = ensoniq->spdif_stream = + SNDRV_PCM_DEFAULT_CON_SPDIF; + outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); + + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) + index++; + + for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) { + kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq); + if (!kctl) + return -ENOMEM; + kctl->id.index = index; + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; } + } if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) { /* mirror rear to front speakers */ ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); @@ -1676,12 +1691,10 @@ static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int h if (err < 0) return err; } - if (((ensoniq->subsystem_vendor_id == 0x1274) && - (ensoniq->subsystem_device_id == 0x2000)) || /* GA-7DXR */ - ((ensoniq->subsystem_vendor_id == 0x1458) && - (ensoniq->subsystem_device_id == 0xa000)) || /* GA-8IEXP */ - has_line > 0) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, ensoniq)); + if (has_line > 0 || + snd_pci_quirk_lookup(ensoniq->pci, ens1373_line_quirk)) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, + ensoniq)); if (err < 0) return err; } @@ -1956,21 +1969,15 @@ static int snd_ensoniq_dev_free(struct snd_device *device) } #ifdef CHIP1371 -static struct { - unsigned short svid; /* subsystem vendor ID */ - unsigned short sdid; /* subsystem device ID */ -} es1371_amplifier_hack[] = { - { .svid = 0x107b, .sdid = 0x2150 }, /* Gateway Solo 2150 */ - { .svid = 0x13bd, .sdid = 0x100c }, /* EV1938 on Mebius PC-MJ100V */ - { .svid = 0x1102, .sdid = 0x5938 }, /* Targa Xtender300 */ - { .svid = 0x1102, .sdid = 0x8938 }, /* IPC Topnote G notebook */ - { .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID } +static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = { + SND_PCI_QUIRK_ID(0x107b, 0x2150), /* Gateway Solo 2150 */ + SND_PCI_QUIRK_ID(0x13bd, 0x100c), /* EV1938 on Mebius PC-MJ100V */ + SND_PCI_QUIRK_ID(0x1102, 0x5938), /* Targa Xtender300 */ + SND_PCI_QUIRK_ID(0x1102, 0x8938), /* IPC Topnote G notebook */ + { } /* end */ }; -static struct { - unsigned short vid; /* vendor ID */ - unsigned short did; /* device ID */ - unsigned char rev; /* revision */ -} es1371_ac97_reset_hack[] = { + +static struct es1371_quirk es1371_ac97_reset_hack[] = { { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, @@ -1984,7 +1991,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) { #ifdef CHIP1371 int idx; - struct pci_dev *pci = ensoniq->pci; #endif /* this code was part of snd_ensoniq_create before intruduction * of suspend/resume @@ -1999,16 +2005,12 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); outl(0, ES_REG(ensoniq, 1371_LEGACY)); - for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++) - if (pci->vendor == es1371_ac97_reset_hack[idx].vid && - pci->device == es1371_ac97_reset_hack[idx].did && - ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { - outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); - /* need to delay around 20ms(bleech) to give - some CODECs enough time to wakeup */ - msleep(20); - break; - } + if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack)) { + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + msleep(20); + } /* AC'97 warm reset to start the bitclk */ outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL)); inl(ES_REG(ensoniq, CONTROL)); @@ -2112,11 +2114,7 @@ static int __devinit snd_ensoniq_create(struct snd_card *card, struct ensoniq ** rensoniq) { struct ensoniq *ensoniq; - unsigned short cmdw; unsigned char cmdb; -#ifdef CHIP1371 - int idx; -#endif int err; static struct snd_device_ops ops = { .dev_free = snd_ensoniq_dev_free, @@ -2159,10 +2157,6 @@ static int __devinit snd_ensoniq_create(struct snd_card *card, pci_set_master(pci); pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb); ensoniq->rev = cmdb; - pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw); - ensoniq->subsystem_vendor_id = cmdw; - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw); - ensoniq->subsystem_device_id = cmdw; #ifdef CHIP1370 #if 0 ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | @@ -2175,19 +2169,11 @@ static int __devinit snd_ensoniq_create(struct snd_card *card, ensoniq->ctrl = 0; ensoniq->sctrl = 0; ensoniq->cssr = 0; - for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++) - if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid && - ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) { - ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ - break; - } - for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++) - if (pci->vendor == es1371_ac97_reset_hack[idx].vid && - pci->device == es1371_ac97_reset_hack[idx].did && - ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { - ensoniq->cssr |= ES_1371_ST_AC97_RST; - break; - } + if (snd_pci_quirk_lookup(pci, es1371_amplifier_hack)) + ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ + + if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack)) + ensoniq->cssr |= ES_1371_ST_AC97_RST; #endif snd_ensoniq_chip_init(ensoniq); -- cgit v1.2.3-59-g8ed1b From a9e996604f77be6f1f4deb0eb1cc2652000054f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 15:42:07 +0100 Subject: [ALSA] intel8x0 - Add spdif_aclink option Added spdif_aclink module option to specify whether the board has SPDIF over AC-link or a direct connection from the controller chip. NForce and ICH4 (or newer) boards may be equipped with SPDIF through AC97 codec. In such a case, SPDIF should be handled as if the old ICH style (the same slot for analog and digital). A quirk list is added to detect this automatically for known hardwares. Corresponds to ALSA bug#2637. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/intel8x0.c | 86 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index f8aef131be7a..a289abfc7172 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -71,6 +71,7 @@ static char *ac97_quirk; static int buggy_semaphore; static int buggy_irq = -1; /* auto-check */ static int xbox; +static int spdif_aclink = -1; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard."); @@ -86,6 +87,8 @@ module_param(buggy_irq, bool, 0444); MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards."); module_param(xbox, bool, 0444); MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection."); +module_param(spdif_aclink, int, 0444); +MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link."); /* just for backward compatibility */ static int enable; @@ -1578,10 +1581,14 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0 *chip) case DEVICE_INTEL_ICH4: tbl = intel_pcms; tblsize = ARRAY_SIZE(intel_pcms); + if (spdif_aclink) + tblsize--; break; case DEVICE_NFORCE: tbl = nforce_pcms; tblsize = ARRAY_SIZE(nforce_pcms); + if (spdif_aclink) + tblsize--; break; case DEVICE_ALI: tbl = ali_pcms; @@ -2040,17 +2047,19 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, }; chip->spdif_idx = -1; /* use PCMOUT (or disabled) */ - switch (chip->device_type) { - case DEVICE_NFORCE: - chip->spdif_idx = NVD_SPBAR; - break; - case DEVICE_ALI: - chip->spdif_idx = ALID_AC97SPDIFOUT; - break; - case DEVICE_INTEL_ICH4: - chip->spdif_idx = ICHD_SPBAR; - break; - }; + if (!spdif_aclink) { + switch (chip->device_type) { + case DEVICE_NFORCE: + chip->spdif_idx = NVD_SPBAR; + break; + case DEVICE_ALI: + chip->spdif_idx = ALID_AC97SPDIFOUT; + break; + case DEVICE_INTEL_ICH4: + chip->spdif_idx = ICHD_SPBAR; + break; + }; + } chip->in_ac97_init = 1; @@ -2173,11 +2182,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, if ((igetdword(chip, ICHREG(GLOB_STA)) & ICH_SAMPLE_CAP) == ICH_SAMPLE_16_20) chip->smp20bit = 1; } - if (chip->device_type == DEVICE_NFORCE) { + if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) { /* 48kHz only */ chip->ichd[chip->spdif_idx].pcm->rates = SNDRV_PCM_RATE_48000; } - if (chip->device_type == DEVICE_INTEL_ICH4) { + if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) { /* use slot 10/11 for SPDIF */ u32 val; val = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK; @@ -2305,7 +2314,7 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing) /* unmute the output on SIS7012 */ iputword(chip, 0x4c, igetword(chip, 0x4c) | 1); } - if (chip->device_type == DEVICE_NFORCE) { + if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) { /* enable SPDIF interrupt */ unsigned int val; pci_read_config_dword(chip->pci, 0x4c, &val); @@ -2398,7 +2407,7 @@ static int snd_intel8x0_free(struct intel8x0 *chip) /* reset channels */ for (i = 0; i < chip->bdbars_count; i++) iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS); - if (chip->device_type == DEVICE_NFORCE) { + if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) { /* stop the spdif interrupt */ unsigned int val; pci_read_config_dword(chip->pci, 0x4c, &val); @@ -2492,7 +2501,7 @@ static int intel8x0_resume(struct pci_dev *pci) snd_intel8x0_chip_init(chip, 0); /* re-initialize mixer stuff */ - if (chip->device_type == DEVICE_INTEL_ICH4) { + if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) { /* enable separate SDINs for ICH4 */ iputbyte(chip, ICHREG(SDM), chip->sdm_saved); /* use slot 10/11 for SPDIF */ @@ -2928,6 +2937,29 @@ static struct shortname_table { { 0, NULL }, }; +static struct snd_pci_quirk spdif_aclink_defaults[] __devinitdata = { + SND_PCI_QUIRK(0x147b, 0x1c1a, "ASUS KN8", 1), + { } /* end */ +}; + +/* look up white/black list for SPDIF over ac-link */ +static int __devinit check_default_spdif_aclink(struct pci_dev *pci) +{ + const struct snd_pci_quirk *w; + + w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults); + if (w) { + if (w->value) + snd_printdd(KERN_INFO "intel8x0: Using SPDIF over " + "AC-Link for %s\n", w->name); + else + snd_printdd(KERN_INFO "intel8x0: Using integrated " + "SPDIF DMA for %s\n", w->name); + return w->value; + } + return 0; +} + static int __devinit snd_intel8x0_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -2940,16 +2972,18 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci, if (card == NULL) return -ENOMEM; - switch (pci_id->driver_data) { - case DEVICE_NFORCE: - strcpy(card->driver, "NFORCE"); - break; - case DEVICE_INTEL_ICH4: - strcpy(card->driver, "ICH4"); - break; - default: - strcpy(card->driver, "ICH"); - break; + if (spdif_aclink < 0) + spdif_aclink = check_default_spdif_aclink(pci); + + strcpy(card->driver, "ICH"); + if (!spdif_aclink) { + switch (pci_id->driver_data) { + case DEVICE_NFORCE: + strcpy(card->driver, "NFORCE"); + break; + case DEVICE_INTEL_ICH4: + strcpy(card->driver, "ICH4"); + } } strcpy(card->shortname, "Intel ICH"); -- cgit v1.2.3-59-g8ed1b From 9f0ac6e1a8677ac509821f4ff0c77d39b1d63125 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 24 Nov 2006 15:49:39 +0100 Subject: [ALSA] Update AT91 ASoC driver for 2.6.19 kernel. Changes were required to support latest AT91 header files. Also updated to remove AT91RM9200-specific code in the ASoC platform drivers to support the AT91SAM9260 and AT91SAM9261 chips, but no testing was performed on these chips. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/Makefile | 4 +- sound/soc/at91/at91-i2s.c | 673 +++++++++++++++++++++++++++++++++++++ sound/soc/at91/at91-pcm.c | 427 ++++++++++++++++++++++++ sound/soc/at91/at91-pcm.h | 71 ++++ sound/soc/at91/at91rm9200-i2s.c | 715 ---------------------------------------- sound/soc/at91/at91rm9200-pcm.c | 428 ------------------------ sound/soc/at91/at91rm9200-pcm.h | 75 ----- sound/soc/at91/eti_b1_wm8731.c | 64 +++- 8 files changed, 1224 insertions(+), 1233 deletions(-) create mode 100644 sound/soc/at91/at91-i2s.c create mode 100644 sound/soc/at91/at91-pcm.c create mode 100644 sound/soc/at91/at91-pcm.h delete mode 100644 sound/soc/at91/at91rm9200-i2s.c delete mode 100644 sound/soc/at91/at91rm9200-pcm.c delete mode 100644 sound/soc/at91/at91rm9200-pcm.h diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile index eb12ea2d1944..b77b01ab2028 100644 --- a/sound/soc/at91/Makefile +++ b/sound/soc/at91/Makefile @@ -1,6 +1,6 @@ # AT91 Platform Support -snd-soc-at91-objs := at91rm9200-pcm.o -snd-soc-at91-i2s-objs := at91rm9200-i2s.o +snd-soc-at91-objs := at91-pcm.o +snd-soc-at91-i2s-objs := at91-i2s.o obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o obj-$(CONFIG_SND_AT91_SOC_I2S) += snd-soc-at91-i2s.o diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-i2s.c new file mode 100644 index 000000000000..b452e8e6a724 --- /dev/null +++ b/sound/soc/at91/at91-i2s.c @@ -0,0 +1,673 @@ +/* + * at91-i2s.c -- ALSA SoC I2S Audio Layer Platform driver + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * + * Based on pxa2xx Platform drivers by + * Liam Girdwood + * + * 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. + * + * Revision history + * 3rd Mar 2006 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "at91-pcm.h" + +#if 0 +#define DBG(x...) printk(KERN_DEBUG "at91-i2s:" x) +#else +#define DBG(x...) +#endif + +#if defined(CONFIG_ARCH_AT91SAM9260) +#define NUM_SSC_DEVICES 1 +#else +#define NUM_SSC_DEVICES 3 +#endif + + +#define AT91_I2S_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) + +#define AT91_I2S_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +/* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */ +static struct snd_soc_dai_mode at91_i2s[] = { + + /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ + { + .fmt = AT91_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_8000, + .pcmdir = AT91_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 1500, + .bfs = SND_SOC_FSBD(10), + .priv = (25 << 16 | 74), + }, + + /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ + { + .fmt = AT91_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_16000, + .pcmdir = AT91_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 750, + .bfs = SND_SOC_FSBD(3), + .priv = (7 << 16 | 133), + }, + + /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ + { + .fmt = AT91_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_32000, + .pcmdir = AT91_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 375, + .bfs = SND_SOC_FSBD(3), + .priv = (7 << 16 | 66), + }, + + /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ + { + .fmt = AT91_I2S_DAIFMT, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, + .pcmrate = SNDRV_PCM_RATE_48000, + .pcmdir = AT91_I2S_DIR, + .flags = SND_SOC_DAI_BFS_DIV, + .fs = 250, + .bfs SND_SOC_FSBD(5), + .priv = (13 << 16 | 23), + }, +}; + + +/* + * SSC PDC registers required by the PCM DMA engine. + */ +static struct at91_pdc_regs pdc_tx_reg = { + .xpr = AT91_PDC_TPR, + .xcr = AT91_PDC_TCR, + .xnpr = AT91_PDC_TNPR, + .xncr = AT91_PDC_TNCR, +}; + +static struct at91_pdc_regs pdc_rx_reg = { + .xpr = AT91_PDC_RPR, + .xcr = AT91_PDC_RCR, + .xnpr = AT91_PDC_RNPR, + .xncr = AT91_PDC_RNCR, +}; + +/* + * SSC & PDC status bits for transmit and receive. + */ +static struct at91_ssc_mask ssc_tx_mask = { + .ssc_enable = AT91_SSC_TXEN, + .ssc_disable = AT91_SSC_TXDIS, + .ssc_endx = AT91_SSC_ENDTX, + .ssc_endbuf = AT91_SSC_TXBUFE, + .pdc_enable = AT91_PDC_TXTEN, + .pdc_disable = AT91_PDC_TXTDIS, +}; + +static struct at91_ssc_mask ssc_rx_mask = { + .ssc_enable = AT91_SSC_RXEN, + .ssc_disable = AT91_SSC_RXDIS, + .ssc_endx = AT91_SSC_ENDRX, + .ssc_endbuf = AT91_SSC_RXBUFF, + .pdc_enable = AT91_PDC_RXTEN, + .pdc_disable = AT91_PDC_RXTDIS, +}; + + +/* + * DMA parameters. + */ +static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { + {{ + .name = "SSC0/I2S PCM Stereo out", + .pdc = &pdc_tx_reg, + .mask = &ssc_tx_mask, + }, + { + .name = "SSC0/I2S PCM Stereo in", + .pdc = &pdc_rx_reg, + .mask = &ssc_rx_mask, + }}, +#if NUM_SSC_DEVICES == 3 + {{ + .name = "SSC1/I2S PCM Stereo out", + .pdc = &pdc_tx_reg, + .mask = &ssc_tx_mask, + }, + { + .name = "SSC1/I2S PCM Stereo in", + .pdc = &pdc_rx_reg, + .mask = &ssc_rx_mask, + }}, + {{ + .name = "SSC2/I2S PCM Stereo out", + .pdc = &pdc_tx_reg, + .mask = &ssc_tx_mask, + }, + { + .name = "SSC1/I2S PCM Stereo in", + .pdc = &pdc_rx_reg, + .mask = &ssc_rx_mask, + }}, +#endif +}; + + +/* + * A MUTEX is used to protect an SSC initialzed flag which allows + * the substream hw_params() call to initialize the SSC only if + * there are no other substreams open. If there are other + * substreams open, the hw_param() call can only check that + * it is using the same format and rate. + */ +static DECLARE_MUTEX(ssc0_mutex); +#if NUM_SSC_DEVICES == 3 +static DECLARE_MUTEX(ssc1_mutex); +static DECLARE_MUTEX(ssc2_mutex); +#endif + + +struct at91_ssc_state { + u32 ssc_cmr; + u32 ssc_rcmr; + u32 ssc_rfmr; + u32 ssc_tcmr; + u32 ssc_tfmr; + u32 ssc_sr; + u32 ssc_imr; +}; + + +static struct at91_ssc_info { + char *name; + struct at91_ssc_periph ssc; + spinlock_t lock; /* lock for dir_mask */ + int dir_mask; /* 0=unused, 1=playback, 2=capture */ + struct semaphore *mutex; + int initialized; + int pcmfmt; + int rate; + struct at91_pcm_dma_params *dma_params[2]; + struct at91_ssc_state ssc_state; + +} ssc_info[NUM_SSC_DEVICES] = { + { + .name = "ssc0", + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc0_mutex, + .initialized = 0, + }, +#if NUM_SSC_DEVICES == 3 + { + .name = "ssc1", + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc1_mutex, + .initialized = 0, + }, + { + .name = "ssc2", + .lock = SPIN_LOCK_UNLOCKED, + .dir_mask = 0, + .mutex = &ssc2_mutex, + .initialized = 0, + }, +#endif +}; + + +static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) +{ + struct at91_ssc_info *ssc_p = dev_id; + struct at91_pcm_dma_params *dma_params; + u32 ssc_sr; + int i; + + ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR) + & at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); + + /* + * Loop through the substreams attached to this SSC. If + * a DMA-related interrupt occurred on that substream, call + * the DMA interrupt handler function, if one has been + * registered in the dma_params structure by the PCM driver. + */ + for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { + dma_params = ssc_p->dma_params[i]; + + if (dma_params != NULL && dma_params->dma_intr_handler != NULL && + (ssc_sr & + (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf))) + + dma_params->dma_intr_handler(ssc_sr, dma_params->substream); + } + + return IRQ_HANDLED; +} + +static int at91_i2s_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct at91_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; + int dir_mask; + + DBG("i2s_startup: SSC_SR=0x%08lx\n", + at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); + dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; + + spin_lock_irq(&ssc_p->lock); + if (ssc_p->dir_mask & dir_mask) { + spin_unlock_irq(&ssc_p->lock); + return -EBUSY; + } + ssc_p->dir_mask |= dir_mask; + spin_unlock_irq(&ssc_p->lock); + + return 0; +} + +static void at91_i2s_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct at91_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; + struct at91_pcm_dma_params *dma_params = rtd->cpu_dai->dma_data; + int dir, dir_mask; + + dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + if (dma_params != NULL) { + at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, + dma_params->mask->ssc_disable); + DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"), + at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); + + dma_params->ssc_base = NULL; + dma_params->substream = NULL; + ssc_p->dma_params[dir] = NULL; + } + + dir_mask = 1 << dir; + + spin_lock_irq(&ssc_p->lock); + ssc_p->dir_mask &= ~dir_mask; + if (!ssc_p->dir_mask) { + /* Shutdown the SSC clock. */ + DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); + at91_sys_write(AT91_PMC_PCDR, 1<ssc.pid); + + if (ssc_p->initialized) + free_irq(ssc_p->ssc.pid, ssc_p); + + /* Reset the SSC */ + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); + + /* Force a re-init on the next hw_params() call. */ + ssc_p->initialized = 0; + } + spin_unlock_irq(&ssc_p->lock); +} + +#ifdef CONFIG_PM +static int at91_i2s_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct at91_ssc_info *ssc_p; + + if(!dai->active) + return 0; + + ssc_p = &ssc_info[dai->id]; + + /* Save the status register before disabling transmit and receive. */ + ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); + at91_ssc_write(ssc_p->ssc.base + + AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); + + /* Save the current interrupt mask, then disable unmasked interrupts. */ + ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->state->ssc_imr); + + ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); + ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + + return 0; +} + +static int at91_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct at91_ssc_info *ssc_p; + u32 cr_mask; + + if(!dai->active) + return 0; + + ssc_p = &ssc_info[dai->id]; + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->state->ssc_cmr); + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->state->ssc_imr); + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, + ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | + ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); + + return 0; +} + +#else +#define at91_i2s_suspend NULL +#define at91_i2s_resume NULL +#endif + +static unsigned int at91_i2s_config_sysclk( + struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info, + unsigned int clk) +{ + /* Currently, there is only support for USB (12Mhz) mode */ + if (clk != 12000000) + return 0; + return 12000000; +} + +static int at91_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int id = rtd->cpu_dai->id; + struct at91_ssc_info *ssc_p = &ssc_info[id]; + struct at91_pcm_dma_params *dma_params; + unsigned int pcmfmt, rate; + int dir, channels, bits; + struct clk *mck_clk; + u32 div, period, tfmr, rfmr, tcmr, rcmr; + int ret; + + /* + * Currently, there is only one set of dma params for + * each direction. If more are added, this code will + * have to be changed to select the proper set. + */ + dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + dma_params = &ssc_dma_params[id][dir]; + dma_params->ssc_base = ssc_p->ssc.base; + dma_params->substream = substream; + + ssc_p->dma_params[dir] = dma_params; + rtd->cpu_dai->dma_data = dma_params; + + rate = params_rate(params); + channels = params_channels(params); + + pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt; + switch (pcmfmt) { + case SNDRV_PCM_FMTBIT_S16_LE: + /* likely this is all we'll ever support, but ... */ + bits = 16; + dma_params->pdc_xfer_size = 2; + break; + default: + printk(KERN_WARNING "at91-i2s: unsupported format %x\n", + pcmfmt); + return -EINVAL; + } + + /* Don't allow both SSC substreams to initialize at the same time. */ + down(ssc_p->mutex); + + /* + * If this SSC is alreadly initialized, then this substream must use + * the same format and rate. + */ + if (ssc_p->initialized) { + if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) { + printk(KERN_WARNING "at91-i2s: " + "incompatible substream in other direction\n"); + up(ssc_p->mutex); + return -EINVAL; + } + } else { + /* Enable PMC peripheral clock for this SSC */ + DBG("Starting pid %d clock\n", ssc_p->ssc.pid); + at91_sys_write(AT91_PMC_PCER, 1<ssc.pid); + + /* Reset the SSC */ + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); + + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RPR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RCR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RNPR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RNCR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TPR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TCR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNPR, 0); + at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNCR, 0); + + div = rtd->cpu_dai->dai_runtime.priv >> 16; + period = rtd->cpu_dai->dai_runtime.priv & 0xffff; + + mck_clk = clk_get(NULL, "mck"); + + DBG("mck %lu fsbd %u bfs %llu bfs_real %u bclk %lu div %u period %u\n", + clk_get_rate(mck_clk), + SND_SOC_FSBD(6), + rtd->cpu_dai->dai_runtime.bfs, + SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), + clk_get_rate(mck_clk) / (2 * div), + div, + period); + + clk_put(mck_clk); + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, div); + + /* + * Setup the TFMR and RFMR for the proper data format. + */ + tfmr = + (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( 0 << 23) & AT91_SSC_FSDEN) + | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) + | (((bits - 1) << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_DATDEF) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + DBG("SSC_TFMR=0x%08x\n", tfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr); + + rfmr = + (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_LOOP) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + DBG("SSC_RFMR=0x%08x\n", rfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr); + + /* + * Setup the TCMR and RCMR to generate the proper BCLK + * and LRC signals. + */ + tcmr = + (( period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + DBG("SSC_TCMR=0x%08x\n", tcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr); + + rcmr = + (( 0 << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); + + DBG("SSC_RCMR=0x%08x\n", rcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr); + + if ((ret = request_irq(ssc_p->ssc.pid, at91_i2s_interrupt, + 0, ssc_p->name, ssc_p)) < 0) { + printk(KERN_WARNING "at91-i2s: request_irq failure\n"); + return ret; + } + + /* + * Save the current substream parameters in order to check + * that the substream in the opposite direction uses the + * same parameters. + */ + ssc_p->pcmfmt = pcmfmt; + ssc_p->rate = rate; + ssc_p->initialized = 1; + + DBG("hw_params: SSC initialized\n"); + } + + up(ssc_p->mutex); + + return 0; +} + + +static int at91_i2s_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct at91_pcm_dma_params *dma_params = rtd->cpu_dai->dma_data; + + at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, + dma_params->mask->ssc_enable); + + DBG("%s enabled SSC_SR=0x%08lx\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive", + at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc.base + AT91_SSC_SR)); + return 0; +} + + +struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { + { .name = "at91_ssc0/i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .suspend = at91_i2s_suspend, + .resume = at91_i2s_resume, + .config_sysclk = at91_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91_i2s_startup, + .shutdown = at91_i2s_shutdown, + .prepare = at91_i2s_prepare, + .hw_params = at91_i2s_hw_params,}, + .caps = { + .mode = &at91_i2s[0], + .num_modes = ARRAY_SIZE(at91_i2s),}, + .private_data = &ssc_info[0].ssc, + }, +#if NUM_SSC_DEVICES == 3 + { .name = "at91_ssc1/i2s", + .id = 1, + .type = SND_SOC_DAI_I2S, + .suspend = at91_i2s_suspend, + .resume = at91_i2s_resume, + .config_sysclk = at91_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91_i2s_startup, + .shutdown = at91_i2s_shutdown, + .prepare = at91_i2s_prepare, + .hw_params = at91_i2s_hw_params,}, + .caps = { + .mode = &at91_i2s[0], + .num_modes = ARRAY_SIZE(at91_i2s),}, + .private_data = &ssc_info[1].ssc, + }, + { .name = "at91_ssc2/i2s", + .id = 2, + .type = SND_SOC_DAI_I2S, + .suspend = at91_i2s_suspend, + .resume = at91_i2s_resume, + .config_sysclk = at91_i2s_config_sysclk, + .playback = { + .channels_min = 1, + .channels_max = 2,}, + .capture = { + .channels_min = 1, + .channels_max = 2,}, + .ops = { + .startup = at91_i2s_startup, + .shutdown = at91_i2s_shutdown, + .prepare = at91_i2s_prepare, + .hw_params = at91_i2s_hw_params,}, + .caps = { + .mode = &at91_i2s[0], + .num_modes = ARRAY_SIZE(at91_i2s),}, + .private_data = &ssc_info[2].ssc, + }, +#endif +}; + +EXPORT_SYMBOL_GPL(at91_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); +MODULE_DESCRIPTION("AT91 I2S ASoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c new file mode 100644 index 000000000000..fd9d7327b239 --- /dev/null +++ b/sound/soc/at91/at91-pcm.c @@ -0,0 +1,427 @@ +/* + * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Mar 3, 2006 + * + * Based on pxa2xx-pcm.c by: + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "at91-pcm.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO "at91-pcm: " x) +#else +#define DBG(x...) +#endif + +static const struct snd_pcm_hardware at91_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 32 * 1024, +}; + +struct at91_runtime_data { + struct at91_pcm_dma_params *params; + dma_addr_t dma_buffer; /* physical address of dma buffer */ + dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ + size_t period_size; + dma_addr_t period_ptr; /* physical address of next period */ + u32 pdc_xpr_save; /* PDC register save */ + u32 pdc_xcr_save; + u32 pdc_xnpr_save; + u32 pdc_xncr_save; +}; + +static void at91_pcm_dma_irq(u32 ssc_sr, + struct snd_pcm_substream *substream) +{ + struct at91_runtime_data *prtd = substream->runtime->private_data; + struct at91_pcm_dma_params *params = prtd->params; + static int count = 0; + + count++; + + if (ssc_sr & params->mask->ssc_endbuf) { + + printk(KERN_WARNING + "at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK + ? "underrun" : "overrun", + params->name, ssc_sr, count); + + /* re-start the PDC */ + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable); + + prtd->period_ptr += prtd->period_size; + if (prtd->period_ptr >= prtd->dma_buffer_end) { + prtd->period_ptr = prtd->dma_buffer; + } + + at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr); + at91_ssc_write(params->ssc_base + params->pdc->xcr, + prtd->period_size / params->pdc_xfer_size); + + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable); + } + + if (ssc_sr & params->mask->ssc_endx) { + + /* Load the PDC next pointer and counter registers */ + prtd->period_ptr += prtd->period_size; + if (prtd->period_ptr >= prtd->dma_buffer_end) { + prtd->period_ptr = prtd->dma_buffer; + } + at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr); + at91_ssc_write(params->ssc_base + params->pdc->xncr, + prtd->period_size / params->pdc_xfer_size); + } + + snd_pcm_period_elapsed(substream); +} + +static int at91_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct at91_runtime_data *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + /* this may get called several times by oss emulation + * with different params */ + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + + prtd->params = rtd->cpu_dai->dma_data; + prtd->params->dma_intr_handler = at91_pcm_dma_irq; + + prtd->dma_buffer = runtime->dma_addr; + prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; + prtd->period_size = params_period_bytes(params); + + DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n", + prtd->params->name, runtime->dma_bytes, prtd->period_size); + return 0; +} + +static int at91_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct at91_runtime_data *prtd = substream->runtime->private_data; + struct at91_pcm_dma_params *params = prtd->params; + + if (params != NULL) { + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable); + prtd->params->dma_intr_handler = NULL; + } + + return 0; +} + +static int at91_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct at91_runtime_data *prtd = substream->runtime->private_data; + struct at91_pcm_dma_params *params = prtd->params; + + at91_ssc_write(params->ssc_base + AT91_SSC_IDR, + params->mask->ssc_endx | params->mask->ssc_endbuf); + + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable); + return 0; +} + +static int at91_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct at91_runtime_data *prtd = substream->runtime->private_data; + struct at91_pcm_dma_params *params = prtd->params; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->period_ptr = prtd->dma_buffer; + + at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr); + at91_ssc_write(params->ssc_base + params->pdc->xcr, + prtd->period_size / params->pdc_xfer_size); + + prtd->period_ptr += prtd->period_size; + at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr); + at91_ssc_write(params->ssc_base + params->pdc->xncr, + prtd->period_size / params->pdc_xfer_size); + + DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n", + (unsigned long) prtd->period_ptr, + at91_ssc_read(params->ssc_base + params->pdc->xpr), + at91_ssc_read(params->ssc_base + params->pdc->xcr), + at91_ssc_read(params->ssc_base + params->pdc->xnpr), + at91_ssc_read(params->ssc_base + params->pdc->xncr)); + + at91_ssc_write(params->ssc_base + AT91_SSC_IER, + params->mask->ssc_endx | params->mask->ssc_endbuf); + + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable); + + DBG("sr=%lx imr=%lx\n", at91_ssc_read(params->ssc_base + AT91_SSC_SR), + at91_ssc_read(params->ssc_base + AT91_SSC_IER)); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable); + break; + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t at91_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct at91_runtime_data *prtd = runtime->private_data; + struct at91_pcm_dma_params *params = prtd->params; + dma_addr_t ptr; + snd_pcm_uframes_t x; + + ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr); + x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); + + if (x == runtime->buffer_size) + x = 0; + return x; +} + +static int at91_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct at91_runtime_data *prtd; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware); + + /* ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + + prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL); + if (prtd == NULL) { + ret = -ENOMEM; + goto out; + } + runtime->private_data = prtd; + + out: + return ret; +} + +static int at91_pcm_close(struct snd_pcm_substream *substream) +{ + struct at91_runtime_data *prtd = substream->runtime->private_data; + + kfree(prtd); + return 0; +} + +static int at91_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +struct snd_pcm_ops at91_pcm_ops = { + .open = at91_pcm_open, + .close = at91_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = at91_pcm_hw_params, + .hw_free = at91_pcm_hw_free, + .prepare = at91_pcm_prepare, + .trigger = at91_pcm_trigger, + .pointer = at91_pcm_pointer, + .mmap = at91_pcm_mmap, +}; + +static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = at91_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + + DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *) buf->area, + (void *) buf->addr, + size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static u64 at91_pcm_dmamask = 0xffffffff; + +static int at91_pcm_new(struct snd_card *card, + struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &at91_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = at91_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = at91_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static int at91_pcm_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct snd_pcm_runtime *runtime = dai->runtime; + struct at91_runtime_data *prtd; + struct at91_pcm_dma_params *params; + + if (!runtime) + return 0; + + prtd = runtime->private_data; + params = prtd->params; + + /* disable the PDC and save the PDC registers */ + + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_disable); + + prtd->pdc_xpr_save = at91_ssc_read(params->ssc_base + params->pdc->xpr); + prtd->pdc_xcr_save = at91_ssc_read(params->ssc_base + params->pdc->xcr); + prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr); + prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr); + + return 0; +} + +static int at91_pcm_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct snd_pcm_runtime *runtime = dai->runtime; + struct at91_runtime_data *prtd; + struct at91_pcm_dma_params *params; + + if (!runtime) + return 0; + + prtd = runtime->private_data; + params = prtd->params; + + /* restore the PDC registers and enable the PDC */ + at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->pdc_xpr_save); + at91_ssc_write(params->ssc_base + params->pdc->xcr, prtd->pdc_xcr_save); + at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save); + at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save); + + at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable); + return 0; +} + +struct snd_soc_platform at91_soc_platform = { + .name = "at91-audio", + .pcm_ops = &at91_pcm_ops, + .pcm_new = at91_pcm_new, + .pcm_free = at91_pcm_free_dma_buffers, + .suspend = at91_pcm_suspend, + .resume = at91_pcm_resume, +}; + +EXPORT_SYMBOL_GPL(at91_soc_platform); + +MODULE_AUTHOR("Frank Mandarino "); +MODULE_DESCRIPTION("Atmel AT91 PCM module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h new file mode 100644 index 000000000000..6c3b095725c8 --- /dev/null +++ b/sound/soc/at91/at91-pcm.h @@ -0,0 +1,71 @@ +/* + * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Mar 3, 2006 + * + * Based on pxa2xx-pcm.h by: + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +struct at91_ssc_periph { + void __iomem *base; + u32 pid; +}; + + +/* + * Registers and status bits that are required by the PCM driver. + */ +struct at91_pdc_regs { + unsigned int xpr; /* PDC recv/trans pointer */ + unsigned int xcr; /* PDC recv/trans counter */ + unsigned int xnpr; /* PDC next recv/trans pointer */ + unsigned int xncr; /* PDC next recv/trans counter */ + unsigned int ptcr; /* PDC transfer control */ +}; + +struct at91_ssc_mask { + u32 ssc_enable; /* SSC recv/trans enable */ + u32 ssc_disable; /* SSC recv/trans disable */ + u32 ssc_endx; /* SSC ENDTX or ENDRX */ + u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ + u32 pdc_enable; /* PDC recv/trans enable */ + u32 pdc_disable; /* PDC recv/trans disable */ +}; + + +/* + * This structure, shared between the PCM driver and the interface, + * contains all information required by the PCM driver to perform the + * PDC DMA operation. All fields except dma_intr_handler() are initialized + * by the interface. The dms_intr_handler() pointer is set by the PCM + * driver and called by the interface SSC interrupt handler if it is + * non-NULL. + */ +struct at91_pcm_dma_params { + char *name; /* stream identifier */ + int pdc_xfer_size; /* PDC counter increment in bytes */ + void __iomem *ssc_base; /* SSC base address */ + struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */ + struct at91_ssc_mask *mask;/* SSC & PDC status bits */ + struct snd_pcm_substream *substream; + void (*dma_intr_handler)(u32, struct snd_pcm_substream *); +}; + +extern struct snd_soc_cpu_dai at91_i2s_dai[3]; +extern struct snd_soc_platform at91_soc_platform; + + +#define at91_ssc_read(a) ((unsigned long) __raw_readl(a)) +#define at91_ssc_write(a,v) __raw_writel((v),(a)) diff --git a/sound/soc/at91/at91rm9200-i2s.c b/sound/soc/at91/at91rm9200-i2s.c deleted file mode 100644 index e3e6345fc8be..000000000000 --- a/sound/soc/at91/at91rm9200-i2s.c +++ /dev/null @@ -1,715 +0,0 @@ -/* - * at91rm9200-i2s.c -- ALSA Soc Audio Layer Platform driver and DMA engine - * - * Author: Frank Mandarino - * Endrelia Technologies Inc. - * - * Based on pxa2xx Platform drivers by - * Liam Girdwood - * - * 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. - * - * Revision history - * 3rd Mar 2006 Initial version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "at91rm9200-pcm.h" - -#if 0 -#define DBG(x...) printk(KERN_DEBUG "at91rm9200-i2s:" x) -#else -#define DBG(x...) -#endif - -#define AT91RM9200_I2S_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) - -#define AT91RM9200_I2S_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -/* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */ -static struct snd_soc_dai_mode at91rm9200_i2s[] = { - - /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ - { - .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, - .bfs = SND_SOC_FSBD(10), - .priv = (25 << 16 | 74), - }, - - /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { - .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 750, - .bfs = SND_SOC_FSBD(3), - .priv = (7 << 16 | 133), - }, - - /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { - .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 375, - .bfs = SND_SOC_FSBD(3), - .priv = (7 << 16 | 66), - }, - - /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ - { - .fmt = AT91RM9200_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = AT91RM9200_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, - .bfs SND_SOC_FSBD(5), - .priv = (13 << 16 | 23), - }, -}; - - -/* - * SSC registers required by the PCM DMA engine. - */ -static struct at91rm9200_ssc_regs ssc_reg[3] = { - { - .cr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_CR), - .ier = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IER), - .idr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IDR), - }, - { - .cr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_CR), - .ier = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IER), - .idr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IDR), - }, - { - .cr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_CR), - .ier = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IER), - .idr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IDR), - }, -}; - -static struct at91rm9200_pdc_regs pdc_tx_reg[3] = { - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), - }, - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), - }, - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), - }, -}; - -static struct at91rm9200_pdc_regs pdc_rx_reg[3] = { - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), - }, - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), - }, - { - .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RPR), - .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RCR), - .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNPR), - .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNCR), - .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), - }, -}; - -/* - * SSC & PDC status bits for transmit and receive. - */ -static struct at91rm9200_ssc_mask ssc_tx_mask = { - .ssc_enable = AT91_SSC_TXEN, - .ssc_disable = AT91_SSC_TXDIS, - .ssc_endx = AT91_SSC_ENDTX, - .ssc_endbuf = AT91_SSC_TXBUFE, - .pdc_enable = AT91_PDC_TXTEN, - .pdc_disable = AT91_PDC_TXTDIS, -}; - -static struct at91rm9200_ssc_mask ssc_rx_mask = { - .ssc_enable = AT91_SSC_RXEN, - .ssc_disable = AT91_SSC_RXDIS, - .ssc_endx = AT91_SSC_ENDRX, - .ssc_endbuf = AT91_SSC_RXBUFF, - .pdc_enable = AT91_PDC_RXTEN, - .pdc_disable = AT91_PDC_RXTDIS, -}; - -/* - * A MUTEX is used to protect an SSC initialzed flag which allows - * the substream hw_params() call to initialize the SSC only if - * there are no other substreams open. If there are other - * substreams open, the hw_param() call can only check that - * it is using the same format and rate. - */ -static DECLARE_MUTEX(ssc0_mutex); -static DECLARE_MUTEX(ssc1_mutex); -static DECLARE_MUTEX(ssc2_mutex); - -/* - * DMA parameters. - */ -static at91rm9200_pcm_dma_params_t ssc_dma_params[3][2] = { - {{ - .name = "SSC0/I2S PCM Stereo out", - .ssc = &ssc_reg[0], - .pdc = &pdc_tx_reg[0], - .mask = &ssc_tx_mask, - }, - { - .name = "SSC0/I2S PCM Stereo in", - .ssc = &ssc_reg[0], - .pdc = &pdc_rx_reg[0], - .mask = &ssc_rx_mask, - }}, - {{ - .name = "SSC1/I2S PCM Stereo out", - .ssc = &ssc_reg[1], - .pdc = &pdc_tx_reg[1], - .mask = &ssc_tx_mask, - }, - { - .name = "SSC1/I2S PCM Stereo in", - .ssc = &ssc_reg[1], - .pdc = &pdc_rx_reg[1], - .mask = &ssc_rx_mask, - }}, - {{ - .name = "SSC2/I2S PCM Stereo out", - .ssc = &ssc_reg[2], - .pdc = &pdc_tx_reg[2], - .mask = &ssc_tx_mask, - }, - { - .name = "SSC1/I2S PCM Stereo in", - .ssc = &ssc_reg[2], - .pdc = &pdc_rx_reg[2], - .mask = &ssc_rx_mask, - }}, -}; - - -struct at91rm9200_ssc_state { - u32 ssc_cmr; - u32 ssc_rcmr; - u32 ssc_rfmr; - u32 ssc_tcmr; - u32 ssc_tfmr; - u32 ssc_sr; - u32 ssc_imr; -}; - -static struct at91rm9200_ssc_info { - char *name; - void __iomem *ssc_base; - u32 pid; - spinlock_t lock; /* lock for dir_mask */ - int dir_mask; /* 0=unused, 1=playback, 2=capture */ - struct semaphore *mutex; - int initialized; - int pcmfmt; - int rate; - at91rm9200_pcm_dma_params_t *dma_params[2]; - struct at91rm9200_ssc_state ssc_state; - -} ssc_info[3] = { - { - .name = "ssc0", - .ssc_base = (void __iomem *) AT91_VA_BASE_SSC0, - .pid = AT91_ID_SSC0, - .lock = SPIN_LOCK_UNLOCKED, - .dir_mask = 0, - .mutex = &ssc0_mutex, - .initialized = 0, - }, - { - .name = "ssc1", - .ssc_base = (void __iomem *) AT91_VA_BASE_SSC1, - .pid = AT91_ID_SSC1, - .lock = SPIN_LOCK_UNLOCKED, - .dir_mask = 0, - .mutex = &ssc1_mutex, - .initialized = 0, - }, - { - .name = "ssc2", - .ssc_base = (void __iomem *) AT91_VA_BASE_SSC2, - .pid = AT91_ID_SSC2, - .lock = SPIN_LOCK_UNLOCKED, - .dir_mask = 0, - .mutex = &ssc2_mutex, - .initialized = 0, - }, -}; - - -static irqreturn_t at91rm9200_i2s_interrupt(int irq, void *dev_id) -{ - struct at91rm9200_ssc_info *ssc_p = dev_id; - at91rm9200_pcm_dma_params_t *dma_params; - u32 ssc_sr; - int i; - - ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR) - & at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); - - /* - * Loop through the substreams attached to this SSC. If - * a DMA-related interrupt occurred on that substream, call - * the DMA interrupt handler function, if one has been - * registered in the dma_params structure by the PCM driver. - */ - for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { - dma_params = ssc_p->dma_params[i]; - - if (dma_params != NULL && dma_params->dma_intr_handler != NULL && - (ssc_sr & - (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf))) - - dma_params->dma_intr_handler(ssc_sr, dma_params->substream); - } - - return IRQ_HANDLED; -} - -static int at91rm9200_i2s_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; - int dir_mask; - - DBG("i2s_startup: SSC_SR=0x%08lx\n", - at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); - dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; - - spin_lock_irq(&ssc_p->lock); - if (ssc_p->dir_mask & dir_mask) { - spin_unlock_irq(&ssc_p->lock); - return -EBUSY; - } - ssc_p->dir_mask |= dir_mask; - spin_unlock_irq(&ssc_p->lock); - - return 0; -} - -static void at91rm9200_i2s_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; - at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; - int dir, dir_mask; - - dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; - - if (dma_params != NULL) { - at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_disable); - DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"), - at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); - - dma_params->substream = NULL; - ssc_p->dma_params[dir] = NULL; - } - - dir_mask = 1 << dir; - - spin_lock_irq(&ssc_p->lock); - ssc_p->dir_mask &= ~dir_mask; - if (!ssc_p->dir_mask) { - /* Shutdown the SSC clock. */ - DBG("Stopping pid %d clock\n", ssc_p->pid); - at91_sys_write(AT91_PMC_PCDR, 1<pid); - - if (ssc_p->initialized) - free_irq(ssc_p->pid, ssc_p); - - /* Reset the SSC */ - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); - - /* Force a re-init on the next hw_params() call. */ - ssc_p->initialized = 0; - } - spin_unlock_irq(&ssc_p->lock); -} - -#ifdef CONFIG_PM -static int at91rm9200_i2s_suspend(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) -{ - struct at91rm9200_ssc_info *ssc_p; - - if(!dai->active) - return 0; - - ssc_p = &ssc_info[dai->id]; - - /* Save the status register before disabling transmit and receive. */ - ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR); - at91_ssc_write(ssc_p->ssc_base + - AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); - - /* Save the current interrupt mask, then disable unmasked interrupts. */ - ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IDR, ssc_p->state->ssc_imr); - - ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_CMR); - ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); - ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); - ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); - ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); - - return 0; -} - -static int at91rm9200_i2s_resume(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) -{ - struct at91rm9200_ssc_info *ssc_p; - u32 cr_mask; - - if(!dai->active) - return 0; - - ssc_p = &ssc_info[dai->id]; - - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, ssc_p->state->ssc_cmr); - - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IER, ssc_p->state->ssc_imr); - - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, - ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | - ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); - - return 0; -} - -#else -#define at91rm9200_i2s_suspend NULL -#define at91rm9200_i2s_resume NULL -#endif - -static unsigned int at91rm9200_i2s_config_sysclk( - struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info, - unsigned int clk) -{ - /* Currently, there is only support for USB (12Mhz) mode */ - if (clk != 12000000) - return 0; - return 12000000; -} - -static int at91rm9200_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int id = rtd->cpu_dai->id; - struct at91rm9200_ssc_info *ssc_p = &ssc_info[id]; - at91rm9200_pcm_dma_params_t *dma_params; - unsigned int pcmfmt, rate; - int dir, channels, bits; - struct clk *mck_clk; - unsigned long bclk; - u32 div, period, tfmr, rfmr, tcmr, rcmr; - int ret; - - /* - * Currently, there is only one set of dma params for - * each direction. If more are added, this code will - * have to be changed to select the proper set. - */ - dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; - - dma_params = &ssc_dma_params[id][dir]; - dma_params->substream = substream; - - ssc_p->dma_params[dir] = dma_params; - rtd->cpu_dai->dma_data = dma_params; - - rate = params_rate(params); - channels = params_channels(params); - - pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt; - switch (pcmfmt) { - case SNDRV_PCM_FMTBIT_S16_LE: - /* likely this is all we'll ever support, but ... */ - bits = 16; - dma_params->pdc_xfer_size = 2; - break; - default: - printk(KERN_WARNING "at91rm9200-i2s: unsupported format %x\n", - pcmfmt); - return -EINVAL; - } - - /* Don't allow both SSC substreams to initialize at the same time. */ - down(ssc_p->mutex); - - /* - * If this SSC is alreadly initialized, then this substream must use - * the same format and rate. - */ - if (ssc_p->initialized) { - if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) { - printk(KERN_WARNING "at91rm9200-i2s: " - "incompatible substream in other direction\n"); - up(ssc_p->mutex); - return -EINVAL; - } - } else { - /* Enable PMC peripheral clock for this SSC */ - DBG("Starting pid %d clock\n", ssc_p->pid); - at91_sys_write(AT91_PMC_PCER, 1<pid); - - /* Reset the SSC */ - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); - - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RPR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RCR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNPR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNCR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TPR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TCR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNPR, 0); - at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNCR, 0); - - mck_clk = clk_get(NULL, "mck"); - - div = rtd->cpu_dai->dai_runtime.priv >> 16; - period = rtd->cpu_dai->dai_runtime.priv & 0xffff; - bclk = 60000000 / (2 * div); - - DBG("mck %ld fsbd %d bfs %d bfs_real %d bclk %ld div %d period %d\n", - clk_get_rate(mck_clk), - SND_SOC_FSBD(6), - rtd->cpu_dai->dai_runtime.bfs, - SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), - bclk, - div, - period); - - clk_put(mck_clk); - - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, div); - - /* - * Setup the TFMR and RFMR for the proper data format. - */ - tfmr = - (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) - | (( 0 << 23) & AT91_SSC_FSDEN) - | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) - | (((bits - 1) << 16) & AT91_SSC_FSLEN) - | (((channels - 1) << 8) & AT91_SSC_DATNB) - | (( 1 << 7) & AT91_SSC_MSBF) - | (( 0 << 5) & AT91_SSC_DATDEF) - | (((bits - 1) << 0) & AT91_SSC_DATALEN); - DBG("SSC_TFMR=0x%08x\n", tfmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TFMR, tfmr); - - rfmr = - (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) - | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) - | (( 0 << 16) & AT91_SSC_FSLEN) - | (((channels - 1) << 8) & AT91_SSC_DATNB) - | (( 1 << 7) & AT91_SSC_MSBF) - | (( 0 << 5) & AT91_SSC_LOOP) - | (((bits - 1) << 0) & AT91_SSC_DATALEN); - - DBG("SSC_RFMR=0x%08x\n", rfmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RFMR, rfmr); - - /* - * Setup the TCMR and RCMR to generate the proper BCLK - * and LRC signals. - */ - tcmr = - (( period << 24) & AT91_SSC_PERIOD) - | (( 1 << 16) & AT91_SSC_STTDLY) - | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) - | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) - | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) - | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); - - DBG("SSC_TCMR=0x%08x\n", tcmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TCMR, tcmr); - - rcmr = - (( 0 << 24) & AT91_SSC_PERIOD) - | (( 1 << 16) & AT91_SSC_STTDLY) - | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START) - | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) - | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) - | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); - - DBG("SSC_RCMR=0x%08x\n", rcmr); - at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, rcmr); - - if ((ret = request_irq(ssc_p->pid, at91rm9200_i2s_interrupt, - 0, ssc_p->name, ssc_p)) < 0) { - printk(KERN_WARNING "at91rm9200-i2s: request_irq failure\n"); - return ret; - } - - /* - * Save the current substream parameters in order to check - * that the substream in the opposite direction uses the - * same parameters. - */ - ssc_p->pcmfmt = pcmfmt; - ssc_p->rate = rate; - ssc_p->initialized = 1; - - DBG("hw_params: SSC initialized\n"); - } - - up(ssc_p->mutex); - - return 0; -} - - -static int at91rm9200_i2s_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; - - at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_enable); - - DBG("%s enabled SSC_SR=0x%08lx\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive", - at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc_base + AT91_SSC_SR)); - return 0; -} - - -struct snd_soc_cpu_dai at91rm9200_i2s_dai[] = { - { .name = "at91rm9200-ssc0/i2s", - .id = 0, - .type = SND_SOC_DAI_I2S, - .suspend = at91rm9200_i2s_suspend, - .resume = at91rm9200_i2s_resume, - .config_sysclk = at91rm9200_i2s_config_sysclk, - .playback = { - .channels_min = 1, - .channels_max = 2,}, - .capture = { - .channels_min = 1, - .channels_max = 2,}, - .ops = { - .startup = at91rm9200_i2s_startup, - .shutdown = at91rm9200_i2s_shutdown, - .prepare = at91rm9200_i2s_prepare, - .hw_params = at91rm9200_i2s_hw_params,}, - .caps = { - .mode = &at91rm9200_i2s[0], - .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, - }, - { .name = "at91rm9200-ssc1/i2s", - .id = 1, - .type = SND_SOC_DAI_I2S, - .suspend = at91rm9200_i2s_suspend, - .resume = at91rm9200_i2s_resume, - .config_sysclk = at91rm9200_i2s_config_sysclk, - .playback = { - .channels_min = 1, - .channels_max = 2,}, - .capture = { - .channels_min = 1, - .channels_max = 2,}, - .ops = { - .startup = at91rm9200_i2s_startup, - .shutdown = at91rm9200_i2s_shutdown, - .prepare = at91rm9200_i2s_prepare, - .hw_params = at91rm9200_i2s_hw_params,}, - .caps = { - .mode = &at91rm9200_i2s[0], - .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, - }, - { .name = "at91rm9200-ssc2/i2s", - .id = 2, - .type = SND_SOC_DAI_I2S, - .suspend = at91rm9200_i2s_suspend, - .resume = at91rm9200_i2s_resume, - .config_sysclk = at91rm9200_i2s_config_sysclk, - .playback = { - .channels_min = 1, - .channels_max = 2,}, - .capture = { - .channels_min = 1, - .channels_max = 2,}, - .ops = { - .startup = at91rm9200_i2s_startup, - .shutdown = at91rm9200_i2s_shutdown, - .prepare = at91rm9200_i2s_prepare, - .hw_params = at91rm9200_i2s_hw_params,}, - .caps = { - .mode = &at91rm9200_i2s[0], - .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, - }, -}; - -EXPORT_SYMBOL_GPL(at91rm9200_i2s_dai); - -/* Module information */ -MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); -MODULE_DESCRIPTION("AT91RM9200 I2S ASoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91rm9200-pcm.c b/sound/soc/at91/at91rm9200-pcm.c deleted file mode 100644 index 237bc5f07579..000000000000 --- a/sound/soc/at91/at91rm9200-pcm.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * at91rm9200-pcm.c -- ALSA PCM interface for the Atmel AT91RM9200 chip. - * - * Author: Frank Mandarino - * Endrelia Technologies Inc. - * Created: Mar 3, 2006 - * - * Based on pxa2xx-pcm.c by: - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: (C) 2004 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "at91rm9200-pcm.h" - -#if 0 -#define DBG(x...) printk(KERN_INFO "at91rm9200-pcm: " x) -#else -#define DBG(x...) -#endif - -static const snd_pcm_hardware_t at91rm9200_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .periods_min = 2, - .periods_max = 1024, - .buffer_bytes_max = 32 * 1024, -}; - -struct at91rm9200_runtime_data { - at91rm9200_pcm_dma_params_t *params; - dma_addr_t dma_buffer; /* physical address of dma buffer */ - dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ - size_t period_size; - dma_addr_t period_ptr; /* physical address of next period */ - u32 pdc_xpr_save; /* PDC register save */ - u32 pdc_xcr_save; - u32 pdc_xnpr_save; - u32 pdc_xncr_save; -}; - -static void at91rm9200_pcm_dma_irq(u32 ssc_sr, - struct snd_pcm_substream *substream) -{ - struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; - at91rm9200_pcm_dma_params_t *params = prtd->params; - static int count = 0; - - count++; - - if (ssc_sr & params->mask->ssc_endbuf) { - - printk(KERN_WARNING - "at91rm9200-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? "underrun" : "overrun", - params->name, ssc_sr, count); - - /* re-start the PDC */ - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); - - prtd->period_ptr += prtd->period_size; - if (prtd->period_ptr >= prtd->dma_buffer_end) { - prtd->period_ptr = prtd->dma_buffer; - } - - at91_ssc_write(params->pdc->xpr, prtd->period_ptr); - at91_ssc_write(params->pdc->xcr, - prtd->period_size / params->pdc_xfer_size); - - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); - } - - if (ssc_sr & params->mask->ssc_endx) { - - /* Load the PDC next pointer and counter registers */ - prtd->period_ptr += prtd->period_size; - if (prtd->period_ptr >= prtd->dma_buffer_end) { - prtd->period_ptr = prtd->dma_buffer; - } - at91_ssc_write(params->pdc->xnpr, prtd->period_ptr); - at91_ssc_write(params->pdc->xncr, - prtd->period_size / params->pdc_xfer_size); - } - - snd_pcm_period_elapsed(substream); -} - -static int at91rm9200_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_runtime_t *runtime = substream->runtime; - struct at91rm9200_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - /* this may get called several times by oss emulation - * with different params */ - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = params_buffer_bytes(params); - - prtd->params = rtd->cpu_dai->dma_data; - prtd->params->dma_intr_handler = at91rm9200_pcm_dma_irq; - - prtd->dma_buffer = runtime->dma_addr; - prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; - prtd->period_size = params_period_bytes(params); - - DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n", - prtd->params->name, runtime->dma_bytes, prtd->period_size); - return 0; -} - -static int at91rm9200_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; - at91rm9200_pcm_dma_params_t *params = prtd->params; - - if (params != NULL) { - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); - prtd->params->dma_intr_handler = NULL; - } - - return 0; -} - -static int at91rm9200_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; - at91rm9200_pcm_dma_params_t *params = prtd->params; - - at91_ssc_write(params->ssc->idr, - params->mask->ssc_endx | params->mask->ssc_endbuf); - - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); - return 0; -} - -static int at91rm9200_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; - at91rm9200_pcm_dma_params_t *params = prtd->params; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - prtd->period_ptr = prtd->dma_buffer; - - at91_ssc_write(params->pdc->xpr, prtd->period_ptr); - at91_ssc_write(params->pdc->xcr, - prtd->period_size / params->pdc_xfer_size); - - prtd->period_ptr += prtd->period_size; - at91_ssc_write(params->pdc->xnpr, prtd->period_ptr); - at91_ssc_write(params->pdc->xncr, - prtd->period_size / params->pdc_xfer_size); - - DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n", - (unsigned long) prtd->period_ptr, - at91_ssc_read(params->pdc->xpr), - at91_ssc_read(params->pdc->xcr), - at91_ssc_read(params->pdc->xnpr), - at91_ssc_read(params->pdc->xncr)); - - at91_ssc_write(params->ssc->ier, - params->mask->ssc_endx | params->mask->ssc_endbuf); - - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); - - DBG("sr=%lx imr=%lx\n", at91_ssc_read(params->ssc->ier - 4), - at91_ssc_read(params->ssc->ier + 8)); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); - break; - - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); - break; - - default: - ret = -EINVAL; - } - - return ret; -} - -static snd_pcm_uframes_t at91rm9200_pcm_pointer( - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct at91rm9200_runtime_data *prtd = runtime->private_data; - at91rm9200_pcm_dma_params_t *params = prtd->params; - dma_addr_t ptr; - snd_pcm_uframes_t x; - - ptr = (dma_addr_t) at91_ssc_read(params->pdc->xpr); - x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); - - if (x == runtime->buffer_size) - x = 0; - return x; -} - -static int at91rm9200_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct at91rm9200_runtime_data *prtd; - int ret = 0; - - snd_soc_set_runtime_hwparams(substream, &at91rm9200_pcm_hardware); - - /* ensure that buffer size is a multiple of period size */ - ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - goto out; - - prtd = kzalloc(sizeof(struct at91rm9200_runtime_data), GFP_KERNEL); - if (prtd == NULL) { - ret = -ENOMEM; - goto out; - } - runtime->private_data = prtd; - - out: - return ret; -} - -static int at91rm9200_pcm_close(struct snd_pcm_substream *substream) -{ - struct at91rm9200_runtime_data *prtd = substream->runtime->private_data; - - kfree(prtd); - return 0; -} - -static int at91rm9200_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -struct snd_pcm_ops at91rm9200_pcm_ops = { - .open = at91rm9200_pcm_open, - .close = at91rm9200_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = at91rm9200_pcm_hw_params, - .hw_free = at91rm9200_pcm_hw_free, - .prepare = at91rm9200_pcm_prepare, - .trigger = at91rm9200_pcm_trigger, - .pointer = at91rm9200_pcm_pointer, - .mmap = at91rm9200_pcm_mmap, -}; - -static int at91rm9200_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = at91rm9200_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - - DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", - (void *) buf->area, - (void *) buf->addr, - size); - - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -static u64 at91rm9200_pcm_dmamask = 0xffffffff; - -static int at91rm9200_pcm_new(struct snd_card *card, - struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) -{ - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &at91rm9200_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = 0xffffffff; - - if (dai->playback.channels_min) { - ret = at91rm9200_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (dai->capture.channels_min) { - ret = at91rm9200_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} - -static void at91rm9200_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static int at91rm9200_pcm_suspend(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) -{ - struct snd_pcm_runtime *runtime = dai->runtime; - struct at91rm9200_runtime_data *prtd; - at91rm9200_pcm_dma_params_t *params; - - if (!runtime) - return 0; - - prtd = runtime->private_data; - params = prtd->params; - - /* disable the PDC and save the PDC registers */ - - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable); - - prtd->pdc_xpr_save = at91_ssc_read(params->pdc->xpr); - prtd->pdc_xcr_save = at91_ssc_read(params->pdc->xcr); - prtd->pdc_xnpr_save = at91_ssc_read(params->pdc->xnpr); - prtd->pdc_xncr_save = at91_ssc_read(params->pdc->xncr); - - return 0; -} - -static int at91rm9200_pcm_resume(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) -{ - struct snd_pcm_runtime *runtime = dai->runtime; - struct at91rm9200_runtime_data *prtd; - at91rm9200_pcm_dma_params_t *params; - - if (!runtime) - return 0; - - prtd = runtime->private_data; - params = prtd->params; - - /* restore the PDC registers and enable the PDC */ - at91_ssc_write(params->pdc->xpr, prtd->pdc_xpr_save); - at91_ssc_write(params->pdc->xcr, prtd->pdc_xcr_save); - at91_ssc_write(params->pdc->xnpr, prtd->pdc_xnpr_save); - at91_ssc_write(params->pdc->xncr, prtd->pdc_xncr_save); - - at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable); - return 0; -} - -struct snd_soc_platform at91rm9200_soc_platform = { - .name = "at91rm9200-audio", - .pcm_ops = &at91rm9200_pcm_ops, - .pcm_new = at91rm9200_pcm_new, - .pcm_free = at91rm9200_pcm_free_dma_buffers, - .suspend = at91rm9200_pcm_suspend, - .resume = at91rm9200_pcm_resume, -}; - -EXPORT_SYMBOL_GPL(at91rm9200_soc_platform); - -MODULE_AUTHOR("Frank Mandarino "); -MODULE_DESCRIPTION("Atmel AT91RM9200 PCM module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/at91/at91rm9200-pcm.h b/sound/soc/at91/at91rm9200-pcm.h deleted file mode 100644 index 65468f173771..000000000000 --- a/sound/soc/at91/at91rm9200-pcm.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * at91rm9200-pcm.h - ALSA PCM interface for the Atmel AT91RM9200 chip - * - * Author: Frank Mandarino - * Endrelia Technologies Inc. - * Created: Mar 3, 2006 - * - * Based on pxa2xx-pcm.h by: - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * Registers and status bits that are required by the PCM driver. - */ -struct at91rm9200_ssc_regs { - void __iomem *cr; /* SSC control */ - void __iomem *ier; /* SSC interrupt enable */ - void __iomem *idr; /* SSC interrupt disable */ -}; - -struct at91rm9200_pdc_regs { - void __iomem *xpr; /* PDC recv/trans pointer */ - void __iomem *xcr; /* PDC recv/trans counter */ - void __iomem *xnpr; /* PDC next recv/trans pointer */ - void __iomem *xncr; /* PDC next recv/trans counter */ - void __iomem *ptcr; /* PDC transfer control */ -}; - -struct at91rm9200_ssc_mask { - u32 ssc_enable; /* SSC recv/trans enable */ - u32 ssc_disable; /* SSC recv/trans disable */ - u32 ssc_endx; /* SSC ENDTX or ENDRX */ - u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ - u32 pdc_enable; /* PDC recv/trans enable */ - u32 pdc_disable; /* PDC recv/trans disable */ -}; - - -/* - * This structure, shared between the PCM driver and the interface, - * contains all information required by the PCM driver to perform the - * PDC DMA operation. All fields except dma_intr_handler() are initialized - * by the interface. The dms_intr_handler() pointer is set by the PCM - * driver and called by the interface SSC interrupt handler if it is - * non-NULL. - */ -typedef struct { - char *name; /* stream identifier */ - int pdc_xfer_size; /* PDC counter increment in bytes */ - struct at91rm9200_ssc_regs *ssc; /* SSC register addresses */ - struct at91rm9200_pdc_regs *pdc; /* PDC receive/transmit registers */ - struct at91rm9200_ssc_mask *mask;/* SSC & PDC status bits */ - snd_pcm_substream_t *substream; - void (*dma_intr_handler)(u32, snd_pcm_substream_t *); -} at91rm9200_pcm_dma_params_t; - -extern struct snd_soc_cpu_dai at91rm9200_i2s_dai[3]; -extern struct snd_soc_platform at91rm9200_soc_platform; - - -/* - * SSC I/O helpers. - * E.g., at91_ssc_write(AT91_SSC(1) + AT91_SSC_CR, AT91_SSC_RXEN); - */ -#define AT91_SSC(x) (((x)==0) ? AT91_VA_BASE_SSC0 :\ - ((x)==1) ? AT91_VA_BASE_SSC1 : ((x)==2) ? AT91_VA_BASE_SSC2 : NULL) -#define at91_ssc_read(a) ((unsigned long) __raw_readl(a)) -#define at91_ssc_write(a,v) __raw_writel((v),(a)) diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index d955cacf2d0d..089cdc9e7265 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -1,5 +1,5 @@ /* - * eti_b1_wm8731 -- SoC audio for Endrelia ETI_B1. + * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board. * * Author: Frank Mandarino * Endrelia Technologies Inc. @@ -37,12 +37,12 @@ #include #include -#include -#include #include +#include +#include #include "../codecs/wm8731.h" -#include "at91rm9200-pcm.h" +#include "at91-pcm.h" #if 0 #define DBG(x...) printk(KERN_INFO "eti_b1_wm8731:" x) @@ -50,10 +50,18 @@ #define DBG(x...) #endif +#define AT91_PIO_TF1 (1 << (AT91_PIN_PB6 - PIN_BASE) % 32) +#define AT91_PIO_TK1 (1 << (AT91_PIN_PB7 - PIN_BASE) % 32) +#define AT91_PIO_TD1 (1 << (AT91_PIN_PB8 - PIN_BASE) % 32) +#define AT91_PIO_RD1 (1 << (AT91_PIN_PB9 - PIN_BASE) % 32) +#define AT91_PIO_RK1 (1 << (AT91_PIN_PB10 - PIN_BASE) % 32) +#define AT91_PIO_RF1 (1 << (AT91_PIN_PB11 - PIN_BASE) % 32) + + static struct clk *pck1_clk; static struct clk *pllb_clk; -static int eti_b1_startup(snd_pcm_substream_t *substream) +static int eti_b1_startup(struct snd_pcm_substream *substream) { /* Start PCK1 clock. */ clk_enable(pck1_clk); @@ -62,7 +70,7 @@ static int eti_b1_startup(snd_pcm_substream_t *substream) return 0; } -static void eti_b1_shutdown(snd_pcm_substream_t *substream) +static void eti_b1_shutdown(struct snd_pcm_substream *substream) { /* Stop PCK1 clock. */ clk_disable(pck1_clk); @@ -138,7 +146,7 @@ unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd, static struct snd_soc_dai_link eti_b1_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai = &at91rm9200_i2s_dai[1], + .cpu_dai = &at91_i2s_dai[1], .codec_dai = &wm8731_dai, .init = eti_b1_wm8731_init, .config_sysclk = eti_b1_config_sysclk, @@ -157,7 +165,7 @@ static struct wm8731_setup_data eti_b1_wm8731_setup = { static struct snd_soc_device eti_b1_snd_devdata = { .machine = &snd_soc_machine_eti_b1, - .platform = &at91rm9200_soc_platform, + .platform = &at91_soc_platform, .codec_dev = &soc_codec_dev_wm8731, .codec_data = &eti_b1_wm8731_setup, }; @@ -168,22 +176,41 @@ static int __init eti_b1_init(void) { int ret; u32 ssc_pio_lines; + struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; + + if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) { + DBG("SSC1 memory region is busy\n"); + return -EBUSY; + } + + ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K); + if (!ssc->base) { + DBG("SSC1 memory ioremap failed\n"); + ret = -ENOMEM; + goto fail_release_mem; + } + + ssc->pid = AT91RM9200_ID_SSC1; eti_b1_snd_device = platform_device_alloc("soc-audio", -1); - if (!eti_b1_snd_device) - return -ENOMEM; + if (!eti_b1_snd_device) { + DBG("platform device allocation failed\n"); + ret = -ENOMEM; + goto fail_io_unmap; + } platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata); eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev; ret = platform_device_add(eti_b1_snd_device); if (ret) { + DBG("platform device add failed\n"); platform_device_put(eti_b1_snd_device); - return ret; + goto fail_io_unmap; } - ssc_pio_lines = AT91_PB6_TF1 | AT91_PB7_TK1 | AT91_PB8_TD1 - | AT91_PB9_RD1 /* | AT91_PB10_RK1 | AT91_PB11_RF1 */; + ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1 + | AT91_PIO_RD1 /* | AT91_PIO_RK1 | AT91_PIO_RF1 */; /* Reset all PIO registers and assign lines to peripheral A */ at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines); @@ -211,14 +238,25 @@ static int __init eti_b1_init(void) at91_set_B_periph(AT91_PIN_PA24, 0); return ret; + +fail_io_unmap: + iounmap(ssc->base); +fail_release_mem: + release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); + return ret; } static void __exit eti_b1_exit(void) { + struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; + clk_put(pck1_clk); clk_put(pllb_clk); platform_device_unregister(eti_b1_snd_device); + + iounmap(ssc->base); + release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); } module_init(eti_b1_init); -- cgit v1.2.3-59-g8ed1b From 5b78efd2ef206265aa789485580df9799c54b650 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 16:12:50 +0100 Subject: [ALSA] Fix documentation of ASoC Fixed obsolete *_t typedefs in ASoC documentation. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/soc/codec.txt | 10 +++++----- Documentation/sound/alsa/soc/platform.txt | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt index 47b36cb16840..274657a03e1c 100644 --- a/Documentation/sound/alsa/soc/codec.txt +++ b/Documentation/sound/alsa/soc/codec.txt @@ -170,11 +170,11 @@ The codec driver also supports the following alsa operations:- /* SoC audio ops */ struct snd_soc_ops { - int (*startup)(snd_pcm_substream_t *); - void (*shutdown)(snd_pcm_substream_t *); - int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *); - int (*hw_free)(snd_pcm_substream_t *); - int (*prepare)(snd_pcm_substream_t *); + int (*startup)(struct snd_pcm_substream *); + void (*shutdown)(struct snd_pcm_substream *); + int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); + int (*hw_free)(struct snd_pcm_substream *); + int (*prepare)(struct snd_pcm_substream *); }; Please refer to the alsa driver PCM documentation for details. diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt index c88df261e922..e95b16d5a53b 100644 --- a/Documentation/sound/alsa/soc/platform.txt +++ b/Documentation/sound/alsa/soc/platform.txt @@ -12,12 +12,12 @@ The platform DMA driver optionally supports the following alsa operations:- /* SoC audio ops */ struct snd_soc_ops { - int (*startup)(snd_pcm_substream_t *); - void (*shutdown)(snd_pcm_substream_t *); - int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *); - int (*hw_free)(snd_pcm_substream_t *); - int (*prepare)(snd_pcm_substream_t *); - int (*trigger)(snd_pcm_substream_t *, int); + int (*startup)(struct snd_pcm_substream *); + void (*shutdown)(struct snd_pcm_substream *); + int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); + int (*hw_free)(struct snd_pcm_substream *); + int (*prepare)(struct snd_pcm_substream *); + int (*trigger)(struct snd_pcm_substream *, int); }; The platform driver exports it's DMA functionailty via struct snd_soc_platform:- @@ -31,11 +31,11 @@ struct snd_soc_platform { int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); /* pcm creation and destruction */ - int (*pcm_new)(snd_card_t *, struct snd_soc_codec_dai *, snd_pcm_t *); - void (*pcm_free)(snd_pcm_t *); + int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *); + void (*pcm_free)(struct snd_pcm *); /* platform stream ops */ - snd_pcm_ops_t *pcm_ops; + struct snd_pcm_ops *pcm_ops; }; Please refer to the alsa driver documentation for details of audio DMA. -- cgit v1.2.3-59-g8ed1b From 0b830bac35dd6e3996bee675c3893857da8a4d0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 16:13:57 +0100 Subject: [ALSA] Clean up serial-u16500.c Remove uesless typedefs and clean up the code a bit to follow the standard coding style. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/drivers/serial-u16550.c | 221 +++++++++++++++++++++++------------------- 1 file changed, 123 insertions(+), 98 deletions(-) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 74028b2219c2..3a86a5820726 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -117,13 +117,13 @@ MODULE_PARM_DESC(adaptor, "Type of adaptor."); #define SERIAL_MODE_INPUT_TRIGGERED (1 << 2) #define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3) -typedef struct _snd_uart16550 { +struct snd_uart16550 { struct snd_card *card; struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *midi_output[SNDRV_SERIAL_MAX_OUTS]; struct snd_rawmidi_substream *midi_input[SNDRV_SERIAL_MAX_INS]; - int filemode; //open status of file + int filemode; /* open status of file */ spinlock_t open_lock; @@ -140,39 +140,39 @@ typedef struct _snd_uart16550 { unsigned char old_divisor_msb; unsigned char old_line_ctrl_reg; - // parameter for using of write loop - short int fifo_limit; //used in uart16550 - short int fifo_count; //used in uart16550 + /* parameter for using of write loop */ + short int fifo_limit; /* used in uart16550 */ + short int fifo_count; /* used in uart16550 */ - // type of adaptor + /* type of adaptor */ int adaptor; - // inputs + /* inputs */ int prev_in; unsigned char rstatus; - // outputs + /* outputs */ int prev_out; unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS]; - // write buffer and its writing/reading position + /* write buffer and its writing/reading position */ unsigned char tx_buff[TX_BUFF_SIZE]; int buff_in_count; int buff_in; int buff_out; int drop_on_full; - // wait timer + /* wait timer */ unsigned int timer_running:1; struct timer_list buffer_timer; -} snd_uart16550_t; +}; static struct platform_device *devices[SNDRV_CARDS]; -static inline void snd_uart16550_add_timer(snd_uart16550_t *uart) +static inline void snd_uart16550_add_timer(struct snd_uart16550 *uart) { - if (! uart->timer_running) { + if (!uart->timer_running) { /* timer 38600bps * 10bit * 16byte */ uart->buffer_timer.expires = jiffies + (HZ+255)/256; uart->timer_running = 1; @@ -180,7 +180,7 @@ static inline void snd_uart16550_add_timer(snd_uart16550_t *uart) } } -static inline void snd_uart16550_del_timer(snd_uart16550_t *uart) +static inline void snd_uart16550_del_timer(struct snd_uart16550 *uart) { if (uart->timer_running) { del_timer(&uart->buffer_timer); @@ -189,10 +189,10 @@ static inline void snd_uart16550_del_timer(snd_uart16550_t *uart) } /* This macro is only used in snd_uart16550_io_loop */ -static inline void snd_uart16550_buffer_output(snd_uart16550_t *uart) +static inline void snd_uart16550_buffer_output(struct snd_uart16550 *uart) { unsigned short buff_out = uart->buff_out; - if( uart->buff_in_count > 0 ) { + if (uart->buff_in_count > 0) { outb(uart->tx_buff[buff_out], uart->base + UART_TX); uart->fifo_count++; buff_out++; @@ -206,7 +206,7 @@ static inline void snd_uart16550_buffer_output(snd_uart16550_t *uart) * We don't want to interrupt this, * as we're already handling an interrupt */ -static void snd_uart16550_io_loop(snd_uart16550_t * uart) +static void snd_uart16550_io_loop(struct snd_uart16550 * uart) { unsigned char c, status; int substream; @@ -220,9 +220,8 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart) c = inb(uart->base + UART_RX); /* keep track of last status byte */ - if (c & 0x80) { + if (c & 0x80) uart->rstatus = c; - } /* handle stream switch */ if (uart->adaptor == SNDRV_SERIAL_GENERIC) { @@ -230,14 +229,16 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart) if (c <= SNDRV_SERIAL_MAX_INS && c > 0) substream = c - 1; if (c != 0xf5) - uart->rstatus = 0; /* prevent future bytes from being interpreted as streams */ - } - else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { - snd_rawmidi_receive(uart->midi_input[substream], &c, 1); - } - } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { + /* prevent future bytes from being + interpreted as streams */ + uart->rstatus = 0; + } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) + && uart->midi_input[substream]) + snd_rawmidi_receive(uart->midi_input[substream], + &c, 1); + } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && + uart->midi_input[substream]) snd_rawmidi_receive(uart->midi_input[substream], &c, 1); - } if (status & UART_LSR_OE) snd_printk("%s: Overrun on device at 0x%lx\n", @@ -250,21 +251,20 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart) /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not, buffer is never filled. */ /* Check write status */ - if (status & UART_LSR_THRE) { + if (status & UART_LSR_THRE) uart->fifo_count = 0; - } if (uart->adaptor == SNDRV_SERIAL_MS124W_SA || uart->adaptor == SNDRV_SERIAL_GENERIC) { /* Can't use FIFO, must send only when CTS is true */ status = inb(uart->base + UART_MSR); - while( (uart->fifo_count == 0) && (status & UART_MSR_CTS) && - (uart->buff_in_count > 0) ) { + while (uart->fifo_count == 0 && (status & UART_MSR_CTS) && + uart->buff_in_count > 0) { snd_uart16550_buffer_output(uart); - status = inb( uart->base + UART_MSR ); + status = inb(uart->base + UART_MSR); } } else { /* Write loop */ - while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ + while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ && uart->buff_in_count > 0) /* Do we want to? */ snd_uart16550_buffer_output(uart); } @@ -294,15 +294,16 @@ static void snd_uart16550_io_loop(snd_uart16550_t * uart) */ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id) { - snd_uart16550_t *uart; + struct snd_uart16550 *uart; - uart = (snd_uart16550_t *) dev_id; + uart = dev_id; spin_lock(&uart->open_lock); if (uart->filemode == SERIAL_MODE_NOT_OPENED) { spin_unlock(&uart->open_lock); return IRQ_NONE; } - inb(uart->base + UART_IIR); /* indicate to the UART that the interrupt has been serviced */ + /* indicate to the UART that the interrupt has been serviced */ + inb(uart->base + UART_IIR); snd_uart16550_io_loop(uart); spin_unlock(&uart->open_lock); return IRQ_HANDLED; @@ -312,9 +313,9 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id) static void snd_uart16550_buffer_timer(unsigned long data) { unsigned long flags; - snd_uart16550_t *uart; + struct snd_uart16550 *uart; - uart = (snd_uart16550_t *)data; + uart = (struct snd_uart16550 *)data; spin_lock_irqsave(&uart->open_lock, flags); snd_uart16550_del_timer(uart); snd_uart16550_io_loop(uart); @@ -326,7 +327,7 @@ static void snd_uart16550_buffer_timer(unsigned long data) * return 0 if found * return negative error if not found */ -static int __init snd_uart16550_detect(snd_uart16550_t *uart) +static int __init snd_uart16550_detect(struct snd_uart16550 *uart) { unsigned long io_base = uart->base; int ok; @@ -343,7 +344,8 @@ static int __init snd_uart16550_detect(snd_uart16550_t *uart) return -EBUSY; } - ok = 1; /* uart detected unless one of the following tests should fail */ + /* uart detected unless one of the following tests should fail */ + ok = 1; /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */ c = inb(io_base + UART_IER); @@ -368,7 +370,7 @@ static int __init snd_uart16550_detect(snd_uart16550_t *uart) return ok; } -static void snd_uart16550_do_open(snd_uart16550_t * uart) +static void snd_uart16550_do_open(struct snd_uart16550 * uart) { char byte; @@ -460,7 +462,7 @@ static void snd_uart16550_do_open(snd_uart16550_t * uart) inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */ } -static void snd_uart16550_do_close(snd_uart16550_t * uart) +static void snd_uart16550_do_close(struct snd_uart16550 * uart) { if (uart->irq < 0) snd_uart16550_del_timer(uart); @@ -514,7 +516,7 @@ static void snd_uart16550_do_close(snd_uart16550_t * uart) static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (uart->filemode == SERIAL_MODE_NOT_OPENED) @@ -528,7 +530,7 @@ static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream) static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; @@ -539,24 +541,24 @@ static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream) return 0; } -static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream, int up) +static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream, + int up) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); - if (up) { + if (up) uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED; - } else { + else uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED; - } spin_unlock_irqrestore(&uart->open_lock, flags); } static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (uart->filemode == SERIAL_MODE_NOT_OPENED) @@ -570,7 +572,7 @@ static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream) static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; @@ -581,18 +583,20 @@ static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream) return 0; }; -static inline int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num ) +static inline int snd_uart16550_buffer_can_write(struct snd_uart16550 *uart, + int Num) { - if( uart->buff_in_count + Num < TX_BUFF_SIZE ) + if (uart->buff_in_count + Num < TX_BUFF_SIZE) return 1; else return 0; } -static inline int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) +static inline int snd_uart16550_write_buffer(struct snd_uart16550 *uart, + unsigned char byte) { unsigned short buff_in = uart->buff_in; - if( uart->buff_in_count < TX_BUFF_SIZE ) { + if (uart->buff_in_count < TX_BUFF_SIZE) { uart->tx_buff[buff_in] = byte; buff_in++; buff_in &= TX_BUFF_MASK; @@ -605,12 +609,14 @@ static inline int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned cha return 0; } -static int snd_uart16550_output_byte(snd_uart16550_t *uart, struct snd_rawmidi_substream *substream, unsigned char midi_byte) +static int snd_uart16550_output_byte(struct snd_uart16550 *uart, + struct snd_rawmidi_substream *substream, + unsigned char midi_byte) { - if (uart->buff_in_count == 0 /* Buffer empty? */ + if (uart->buff_in_count == 0 /* Buffer empty? */ && ((uart->adaptor != SNDRV_SERIAL_MS124W_SA && uart->adaptor != SNDRV_SERIAL_GENERIC) || - (uart->fifo_count == 0 /* FIFO empty? */ + (uart->fifo_count == 0 /* FIFO empty? */ && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */ /* Tx Buffer Empty - try to write immediately */ @@ -623,12 +629,13 @@ static int snd_uart16550_output_byte(snd_uart16550_t *uart, struct snd_rawmidi_s uart->fifo_count++; outb(midi_byte, uart->base + UART_TX); } else { - /* Cannot write (buffer empty) - put char in buffer */ + /* Cannot write (buffer empty) - + * put char in buffer */ snd_uart16550_write_buffer(uart, midi_byte); } } } else { - if( !snd_uart16550_write_buffer(uart, midi_byte) ) { + if (!snd_uart16550_write_buffer(uart, midi_byte)) { snd_printk("%s: Buffer overrun on device at 0x%lx\n", uart->rmidi->name, uart->base); return 0; @@ -642,9 +649,9 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) { unsigned long flags; unsigned char midi_byte, addr_byte; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; char first; - static unsigned long lasttime=0; + static unsigned long lasttime = 0; /* Interupts are disabled during the updating of the tx_buff, * since it is 'bad' to have two processes updating the same @@ -653,7 +660,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) spin_lock_irqsave(&uart->open_lock, flags); - if (uart->irq < 0) //polling + if (uart->irq < 0) /* polling */ snd_uart16550_io_loop(uart); if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) { @@ -671,7 +678,8 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) /* select any combination of the four ports */ addr_byte = (substream->number << 4) | 0x08; /* ...except none */ - if (addr_byte == 0x08) addr_byte = 0xf8; + if (addr_byte == 0x08) + addr_byte = 0xf8; #endif snd_uart16550_output_byte(uart, substream, addr_byte); /* send midi byte */ @@ -679,31 +687,42 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) } } else { first = 0; - while( 1 == snd_rawmidi_transmit_peek(substream, &midi_byte, 1) ) { - /* Also send F5 after 3 seconds with no data to handle device disconnect */ - if (first == 0 && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS || - uart->adaptor == SNDRV_SERIAL_GENERIC) && - (uart->prev_out != substream->number || jiffies-lasttime > 3*HZ)) { - - if( snd_uart16550_buffer_can_write( uart, 3 ) ) { + while (snd_rawmidi_transmit_peek(substream, &midi_byte, 1) == 1) { + /* Also send F5 after 3 seconds with no data + * to handle device disconnect */ + if (first == 0 && + (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS || + uart->adaptor == SNDRV_SERIAL_GENERIC) && + (uart->prev_out != substream->number || + jiffies-lasttime > 3*HZ)) { + + if (snd_uart16550_buffer_can_write(uart, 3)) { /* Roland Soundcanvas part selection */ - /* If this substream of the data is different previous - substream in this uart, send the change part event */ + /* If this substream of the data is + * different previous substream + * in this uart, send the change part + * event + */ uart->prev_out = substream->number; /* change part */ - snd_uart16550_output_byte(uart, substream, 0xf5); + snd_uart16550_output_byte(uart, substream, + 0xf5); /* data */ - snd_uart16550_output_byte(uart, substream, uart->prev_out + 1); - /* If midi_byte is a data byte, send the previous status byte */ - if ((midi_byte < 0x80) && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS)) + snd_uart16550_output_byte(uart, substream, + uart->prev_out + 1); + /* If midi_byte is a data byte, + * send the previous status byte */ + if (midi_byte < 0x80 && + uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS) snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]); - } else if( !uart->drop_on_full ) + } else if (!uart->drop_on_full) break; } /* send midi byte */ - if( !snd_uart16550_output_byte(uart, substream, midi_byte) && !uart->drop_on_full ) + if (!snd_uart16550_output_byte(uart, substream, midi_byte) && + !uart->drop_on_full ) break; if (midi_byte >= 0x80 && midi_byte < 0xf0) @@ -717,17 +736,17 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) spin_unlock_irqrestore(&uart->open_lock, flags); } -static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream, int up) +static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream, + int up) { unsigned long flags; - snd_uart16550_t *uart = substream->rmidi->private_data; + struct snd_uart16550 *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); - if (up) { + if (up) uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED; - } else { + else uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED; - } spin_unlock_irqrestore(&uart->open_lock, flags); if (up) snd_uart16550_output_write(substream); @@ -747,10 +766,10 @@ static struct snd_rawmidi_ops snd_uart16550_input = .trigger = snd_uart16550_input_trigger, }; -static int snd_uart16550_free(snd_uart16550_t *uart) +static int snd_uart16550_free(struct snd_uart16550 *uart) { if (uart->irq >= 0) - free_irq(uart->irq, (void *)uart); + free_irq(uart->irq, uart); release_and_free_resource(uart->res_base); kfree(uart); return 0; @@ -758,7 +777,7 @@ static int snd_uart16550_free(snd_uart16550_t *uart) static int snd_uart16550_dev_free(struct snd_device *device) { - snd_uart16550_t *uart = device->device_data; + struct snd_uart16550 *uart = device->device_data; return snd_uart16550_free(uart); } @@ -769,12 +788,12 @@ static int __init snd_uart16550_create(struct snd_card *card, unsigned int base, int adaptor, int droponfull, - snd_uart16550_t **ruart) + struct snd_uart16550 **ruart) { static struct snd_device_ops ops = { .dev_free = snd_uart16550_dev_free, }; - snd_uart16550_t *uart; + struct snd_uart16550 *uart; int err; @@ -795,7 +814,7 @@ static int __init snd_uart16550_create(struct snd_card *card, if (irq >= 0 && irq != SNDRV_AUTO_IRQ) { if (request_irq(irq, snd_uart16550_interrupt, - IRQF_DISABLED, "Serial MIDI", (void *) uart)) { + IRQF_DISABLED, "Serial MIDI", uart)) { snd_printk("irq %d busy. Using Polling.\n", irq); } else { uart->irq = irq; @@ -843,23 +862,28 @@ static int __init snd_uart16550_create(struct snd_card *card, static void __init snd_uart16550_substreams(struct snd_rawmidi_str *stream) { - struct list_head *list; + struct snd_rawmidi_substream *substream; - list_for_each(list, &stream->substreams) { - struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list); + list_for_each_entry(substream, &stream->substreams, list) { sprintf(substream->name, "Serial MIDI %d", substream->number + 1); } } -static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, int ins, struct snd_rawmidi **rmidi) +static int __init snd_uart16550_rmidi(struct snd_uart16550 *uart, int device, + int outs, int ins, + struct snd_rawmidi **rmidi) { struct snd_rawmidi *rrawmidi; int err; - if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, ins, &rrawmidi)) < 0) + err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, + outs, ins, &rrawmidi); + if (err < 0) return err; - snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input); - snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_uart16550_input); + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_uart16550_output); strcpy(rrawmidi->name, "Serial MIDI"); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); @@ -875,7 +899,7 @@ static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int out static int __init snd_serial_probe(struct platform_device *devptr) { struct snd_card *card; - snd_uart16550_t *uart; + struct snd_uart16550 *uart; int err; int dev = devptr->id; @@ -929,7 +953,8 @@ static int __init snd_serial_probe(struct platform_device *devptr) &uart)) < 0) goto _err; - if ((err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi)) < 0) + err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi); + if (err < 0) goto _err; sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d ins %d adaptor %s droponfull %d", -- cgit v1.2.3-59-g8ed1b From f5fcc13c2fc62da6f75d80189a51c2492afb39c0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Nov 2006 17:07:44 +0100 Subject: [ALSA] hda-codec - Use snd_pci_quirk_lookup() for board config lookup Use snd_pci_quirk_lookup() for looking up a board config table. The config table is sorted in numerical order of PCI SSIDs. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/hda_codec.txt | 10 +- sound/pci/hda/hda_codec.c | 57 ++-- sound/pci/hda/hda_local.h | 11 +- sound/pci/hda/patch_analog.c | 145 +++++---- sound/pci/hda/patch_cmedia.c | 24 +- sound/pci/hda/patch_conexant.c | 65 ++-- sound/pci/hda/patch_realtek.c | 550 ++++++++++++++------------------- sound/pci/hda/patch_sigmatel.c | 406 +++++++++++------------- 8 files changed, 580 insertions(+), 688 deletions(-) diff --git a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt index 0be57ed81302..4eaae2a45534 100644 --- a/Documentation/sound/alsa/hda_codec.txt +++ b/Documentation/sound/alsa/hda_codec.txt @@ -277,11 +277,11 @@ Helper Functions snd_hda_get_codec_name() stores the codec name on the given string. snd_hda_check_board_config() can be used to obtain the configuration -information matching with the device. Define the table with struct -hda_board_config entries (zero-terminated), and pass it to the -function. The function checks the modelname given as a module -parameter, and PCI subsystem IDs. If the matching entry is found, it -returns the config field value. +information matching with the device. Define the model string table +and the table with struct snd_pci_quirk entries (zero-terminated), +and pass it to the function. The function checks the modelname given +as a module parameter, and PCI subsystem IDs. If the matching entry +is found, it returns the config field value. snd_hda_add_new_ctls() can be used to create and add control entries. Pass the zero-terminated array of struct snd_kcontrol_new. The same array diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 18bbc87e376f..c07d5db6b050 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1714,6 +1714,8 @@ EXPORT_SYMBOL(snd_hda_build_pcms); /** * snd_hda_check_board_config - compare the current codec with the config table * @codec: the HDA codec + * @num_configs: number of config enums + * @models: array of model name strings * @tbl: configuration table, terminated by null entries * * Compares the modelname or PCI subsystem id of the current codec with the @@ -1722,33 +1724,44 @@ EXPORT_SYMBOL(snd_hda_build_pcms); * * If no entries are matching, the function returns a negative value. */ -int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl) -{ - const struct hda_board_config *c; - - if (codec->bus->modelname) { - for (c = tbl; c->modelname || c->pci_subvendor; c++) { - if (c->modelname && - ! strcmp(codec->bus->modelname, c->modelname)) { - snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname); - return c->config; +int snd_hda_check_board_config(struct hda_codec *codec, + int num_configs, const char **models, + const struct snd_pci_quirk *tbl) +{ + if (codec->bus->modelname && models) { + int i; + for (i = 0; i < num_configs; i++) { + if (models[i] && + !strcmp(codec->bus->modelname, models[i])) { + snd_printd(KERN_INFO "hda_codec: model '%s' is " + "selected\n", models[i]); + return i; } } } - if (codec->bus->pci) { - u16 subsystem_vendor, subsystem_device; - pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); - pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device); - for (c = tbl; c->modelname || c->pci_subvendor; c++) { - if (c->pci_subvendor == subsystem_vendor && - (! c->pci_subdevice /* all match */|| - (c->pci_subdevice == subsystem_device))) { - snd_printdd(KERN_INFO "hda_codec: PCI %x:%x, codec config %d is selected\n", - subsystem_vendor, subsystem_device, c->config); - return c->config; - } + if (!codec->bus->pci || !tbl) + return -1; + + tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl); + if (!tbl) + return -1; + if (tbl->value >= 0 && tbl->value < num_configs) { +#ifdef CONFIG_SND_DEBUG_DETECT + char tmp[10]; + const char *model = NULL; + if (models) + model = models[tbl->value]; + if (!model) { + sprintf(tmp, "#%d", tbl->value); + model = tmp; } + snd_printdd(KERN_INFO "hda_codec: model '%s' is selected " + "for config %x:%x (%s)\n", + model, tbl->subvendor, tbl->subdevice, + (tbl->name ? tbl->name : "Unknown device")); +#endif + return tbl->value; } return -1; } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 9ca1baf860bd..b2f56d688852 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -173,14 +173,9 @@ static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } /* * Misc */ -struct hda_board_config { - const char *modelname; - int config; - unsigned short pci_subvendor; - unsigned short pci_subdevice; -}; - -int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl); +int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, + const char **modelnames, + const struct snd_pci_quirk *pci_list); int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew); /* diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9ce4c9f869b2..2e18a716a095 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -787,55 +787,43 @@ static struct hda_verb ad1986a_eapd_init_verbs[] = { }; /* models */ -enum { AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, AD1986A_LAPTOP_EAPD }; - -static struct hda_board_config ad1986a_cfg_tbl[] = { - { .modelname = "6stack", .config = AD1986A_6STACK }, - { .modelname = "3stack", .config = AD1986A_3STACK }, - { .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84, - .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x817f, - .config = AD1986A_3STACK }, /* ASUS P5P-L2 */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3, - .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb, - .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x8234, - .config = AD1986A_3STACK }, /* ASUS M2N-MX */ - { .pci_subvendor = 0x17aa, .pci_subdevice = 0x1017, - .config = AD1986A_3STACK }, /* Lenovo A60 desktop */ - { .modelname = "laptop", .config = AD1986A_LAPTOP }, - { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, - .config = AD1986A_LAPTOP }, /* FSC V2060 */ - { .pci_subvendor = 0x17c0, .pci_subdevice = 0x2017, - .config = AD1986A_LAPTOP }, /* Samsung M50 */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x818f, - .config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */ - { .modelname = "laptop-eapd", .config = AD1986A_LAPTOP_EAPD }, - { .pci_subvendor = 0x144d, .pci_subdevice = 0xc023, - .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */ - { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024, - .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */ - { .pci_subvendor = 0x144d, .pci_subdevice = 0xc026, - .config = AD1986A_LAPTOP_EAPD }, /* Samsung X11-T2300 Culesa */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1213, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS A6J */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x11f7, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS U5A */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1263, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS U5F */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1297, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x12b3, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS V1j */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1302, - .config = AD1986A_LAPTOP_EAPD }, /* ASUS W3j */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x30af, - .config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */ - { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066, - .config = AD1986A_LAPTOP_EAPD }, /* Lenovo 3000 N100-07684JU */ +enum { + AD1986A_6STACK, + AD1986A_3STACK, + AD1986A_LAPTOP, + AD1986A_LAPTOP_EAPD, + AD1986A_MODELS +}; + +static const char *ad1986a_models[AD1986A_MODELS] = { + [AD1986A_6STACK] = "6stack", + [AD1986A_3STACK] = "3stack", + [AD1986A_LAPTOP] = "laptop", + [AD1986A_LAPTOP_EAPD] = "laptop-eapd", +}; + +static struct snd_pci_quirk ad1986a_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), + SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), + SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), + SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), + SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), + SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), + SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), + SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), {} }; @@ -867,7 +855,9 @@ static int patch_ad1986a(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, ad1986a_cfg_tbl); + board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, + ad1986a_models, + ad1986a_cfg_tbl); switch (board_config) { case AD1986A_3STACK: spec->num_mixers = 2; @@ -1397,20 +1387,27 @@ static struct hda_input_mux ad1981_thinkpad_capture_source = { }; /* models */ -enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD }; +enum { + AD1981_BASIC, + AD1981_HP, + AD1981_THINKPAD, + AD1981_MODELS +}; -static struct hda_board_config ad1981_cfg_tbl[] = { - { .modelname = "hp", .config = AD1981_HP }, +static const char *ad1981_models[AD1981_MODELS] = { + [AD1981_HP] = "hp", + [AD1981_THINKPAD] = "thinkpad", + [AD1981_BASIC] = "basic", +}; + +static struct snd_pci_quirk ad1981_cfg_tbl[] = { /* All HP models */ - { .pci_subvendor = 0x103c, .config = AD1981_HP }, - { .pci_subvendor = 0x30b0, .pci_subdevice = 0x103c, - .config = AD1981_HP }, /* HP nx6320 (reversed SSID, H/W bug) */ - { .modelname = "thinkpad", .config = AD1981_THINKPAD }, + SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), + /* HP nx6320 (reversed SSID, H/W bug) */ + SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), /* Lenovo Thinkpad T60/X60/Z6xx */ - { .pci_subvendor = 0x17aa, .config = AD1981_THINKPAD }, - { .pci_subvendor = 0x1014, .pci_subdevice = 0x0597, - .config = AD1981_THINKPAD }, /* Z60m/t */ - { .modelname = "basic", .config = AD1981_BASIC }, + SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), + SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), {} }; @@ -1443,7 +1440,9 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, ad1981_cfg_tbl); + board_config = snd_hda_check_board_config(codec, AD1981_MODELS, + ad1981_models, + ad1981_cfg_tbl); switch (board_config) { case AD1981_HP: spec->mixers[0] = ad1981_hp_mixers; @@ -2571,15 +2570,14 @@ static int ad1988_auto_init(struct hda_codec *codec) /* */ -static struct hda_board_config ad1988_cfg_tbl[] = { - { .modelname = "6stack", .config = AD1988_6STACK }, - { .modelname = "6stack-dig", .config = AD1988_6STACK_DIG }, - { .modelname = "3stack", .config = AD1988_3STACK }, - { .modelname = "3stack-dig", .config = AD1988_3STACK_DIG }, - { .modelname = "laptop", .config = AD1988_LAPTOP }, - { .modelname = "laptop-dig", .config = AD1988_LAPTOP_DIG }, - { .modelname = "auto", .config = AD1988_AUTO }, - {} +static const char *ad1988_models[AD1988_MODEL_LAST] = { + [AD1988_6STACK] = "6stack", + [AD1988_6STACK_DIG] = "6stack-dig", + [AD1988_3STACK] = "3stack", + [AD1988_3STACK_DIG] = "3stack-dig", + [AD1988_LAPTOP] = "laptop", + [AD1988_LAPTOP_DIG] = "laptop-dig", + [AD1988_AUTO] = "auto", }; static int patch_ad1988(struct hda_codec *codec) @@ -2597,8 +2595,9 @@ static int patch_ad1988(struct hda_codec *codec) if (is_rev2(codec)) snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); - board_config = snd_hda_check_board_config(codec, ad1988_cfg_tbl); - if (board_config < 0 || board_config >= AD1988_MODEL_LAST) { + board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, + ad1988_models, NULL); + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n"); board_config = AD1988_AUTO; } diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index d38ce22507ae..5b9d3a31a1ae 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -40,6 +40,7 @@ enum { CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */ CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */ CMI_AUTO, /* let driver guess it */ + CMI_MODELS }; struct cmi_spec { @@ -603,14 +604,17 @@ static void cmi9880_free(struct hda_codec *codec) /* */ -static struct hda_board_config cmi9880_cfg_tbl[] = { - { .modelname = "minimal", .config = CMI_MINIMAL }, - { .modelname = "min_fp", .config = CMI_MIN_FP }, - { .modelname = "full", .config = CMI_FULL }, - { .modelname = "full_dig", .config = CMI_FULL_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x813d, .config = CMI_FULL_DIG }, /* ASUS P5AD2 */ - { .modelname = "allout", .config = CMI_ALLOUT }, - { .modelname = "auto", .config = CMI_AUTO }, +static const char *cmi9880_models[CMI_MODELS] = { + [CMI_MINIMAL] = "minimal", + [CMI_MIN_FP] = "min_fp", + [CMI_FULL] = "full", + [CMI_FULL_DIG] = "full_dig", + [CMI_ALLOUT] = "allout", + [CMI_AUTO] = "auto", +}; + +static struct snd_pci_quirk cmi9880_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG), {} /* terminator */ }; @@ -633,7 +637,9 @@ static int patch_cmi9880(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; - spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl); + spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS, + cmi9880_models, + cmi9880_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); spec->board_config = CMI_AUTO; /* try everything */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7e7d0c110c4c..dec8f9747fc6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -802,22 +802,22 @@ static int cxt5045_init(struct hda_codec *codec) enum { - CXT5045_LAPTOP, + CXT5045_LAPTOP, /* Laptops w/ EAPD support */ #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif + CXT5045_MODELS }; -static struct hda_board_config cxt5045_cfg_tbl[] = { - /* Laptops w/ EAPD support */ - { .modelname = "laptop", .config = CXT5045_LAPTOP }, - /* HP DV6000Z */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x30b7, - .config = CXT5045_LAPTOP }, +static const char *cxt5045_models[CXT5045_MODELS] = { + [CXT5045_LAPTOP] = "laptop", #ifdef CONFIG_SND_DEBUG - { .modelname = "test", .config = CXT5045_TEST }, + [CXT5045_TEST] = "test", #endif - +}; + +static struct snd_pci_quirk cxt5045_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP), {} }; @@ -852,7 +852,9 @@ static int patch_cxt5045(struct hda_codec *codec) codec->patch_ops = conexant_patch_ops; codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - board_config = snd_hda_check_board_config(codec, cxt5045_cfg_tbl); + board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, + cxt5045_models, + cxt5045_cfg_tbl); switch (board_config) { case CXT5045_LAPTOP: spec->input_mux = &cxt5045_capture_source; @@ -1214,36 +1216,29 @@ static int cxt5047_hp_init(struct hda_codec *codec) enum { - CXT5047_LAPTOP, + CXT5047_LAPTOP, /* Laptops w/o EAPD support */ + CXT5047_LAPTOP_HP, /* Some HP laptops */ + CXT5047_LAPTOP_EAPD, /* Laptops with EAPD support */ #ifdef CONFIG_SND_DEBUG CXT5047_TEST, #endif - CXT5047_LAPTOP_HP, - CXT5047_LAPTOP_EAPD + CXT5047_MODELS }; -static struct hda_board_config cxt5047_cfg_tbl[] = { - /* Laptops w/o EAPD support */ - { .modelname = "laptop", .config = CXT5047_LAPTOP }, - /*HP DV1000 */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x30a0, - .config = CXT5047_LAPTOP }, - /*HP DV2000T/DV3000T */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x30b2, - .config = CXT5047_LAPTOP }, - /* Not all HP's are created equal */ - { .modelname = "laptop-hp", .config = CXT5047_LAPTOP_HP }, - /*HP DV5200TX/DV8000T / Compaq V5209US/V5204NR */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x30a5, - .config = CXT5047_LAPTOP_HP }, - /* Laptops with EAPD support */ - { .modelname = "laptop-eapd", .config = CXT5047_LAPTOP_EAPD }, - { .pci_subvendor = 0x1179, .pci_subdevice = 0xff31, - .config = CXT5047_LAPTOP_EAPD }, /* Toshiba P100 */ +static const char *cxt5047_models[CXT5047_MODELS] = { + [CXT5047_LAPTOP] = "laptop", + [CXT5047_LAPTOP_HP] = "laptop-hp", + [CXT5047_LAPTOP_EAPD] = "laptop-eapd", #ifdef CONFIG_SND_DEBUG - { .modelname = "test", .config = CXT5047_TEST }, + [CXT5047_TEST] = "test", #endif - +}; + +static struct snd_pci_quirk cxt5047_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), {} }; @@ -1277,7 +1272,9 @@ static int patch_cxt5047(struct hda_codec *codec) codec->patch_ops = conexant_patch_ops; codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; - board_config = snd_hda_check_board_config(codec, cxt5047_cfg_tbl); + board_config = snd_hda_check_board_config(codec, CXT5047_MODELS, + cxt5047_models, + cxt5047_cfg_tbl); switch (board_config) { case CXT5047_LAPTOP: break; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 02c465147d98..415a6db4c909 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2328,162 +2328,108 @@ static struct hda_verb alc880_test_init_verbs[] = { /* */ -static struct hda_board_config alc880_cfg_tbl[] = { - /* Back 3 jack, front 2 jack */ - { .modelname = "3stack", .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe212, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe213, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe234, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST }, - /* TCL S700 */ - { .modelname = "tcl", .config = ALC880_TCL_S700 }, - { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 }, - - /* Back 3 jack, front 2 jack (Internal add Aux-In) */ - { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81a0, .config = ALC880_3ST }, - - /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */ - { .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG }, - - /* Clevo laptops */ - { .modelname = "clevo", .config = ALC880_CLEVO }, - { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, - .config = ALC880_CLEVO }, /* Clevo m520G NB */ - { .pci_subvendor = 0x1558, .pci_subdevice = 0x0660, - .config = ALC880_CLEVO }, /* Clevo m665n */ - - /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG }, - - /* Back 5 jack, front 2 jack */ - { .modelname = "5stack", .config = ALC880_5ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST }, - { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST }, - - /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */ - { .modelname = "5stack-digout", .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0xa0a0, .pci_subdevice = 0x0560, - .config = ALC880_5ST_DIG }, /* Aopen i915GMm-HFS */ - /* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */ - { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG }, - /* note subvendor = 0 below */ - /* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */ - - { .modelname = "w810", .config = ALC880_W810 }, - { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 }, - - { .modelname = "z71v", .config = ALC880_Z71V }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V }, - - { .modelname = "6stack", .config = ALC880_6ST }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST }, - { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */ - - { .modelname = "6stack-digout", .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1039, .pci_subdevice = 0x1234, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x0077, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x0078, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x0087, .config = ALC880_6ST_DIG }, - { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */ - { .pci_subvendor = 0x1509, .pci_subdevice = 0x925d, .config = ALC880_6ST_DIG }, /* FIC P4M-915GD1 */ - { .pci_subvendor = 0x1695, .pci_subdevice = 0x4012, .config = ALC880_5ST_DIG }, /* Epox EP-5LDA+ GLi */ - { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST_DIG }, /* Gigabyte K8N51 */ - - { .modelname = "asus", .config = ALC880_ASUS }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c2, .config = ALC880_ASUS_DIG }, /* Asus W6A */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS }, - { .modelname = "asus-w1v", .config = ALC880_ASUS_W1V }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V }, - { .modelname = "asus-dig", .config = ALC880_ASUS_DIG }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */ - { .modelname = "asus-dig2", .config = ALC880_ASUS_DIG2 }, - { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 }, - - { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG }, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG }, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9070, .config = ALC880_UNIWILL }, - { .pci_subvendor = 0x1734, .pci_subdevice = 0x10ac, .config = ALC880_UNIWILL }, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9077, .config = ALC880_UNIWILL_P53 }, - - { .modelname = "F1734", .config = ALC880_F1734 }, - { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 }, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9054, .config = ALC880_F1734 }, - - { .modelname = "lg", .config = ALC880_LG }, - { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG }, - { .pci_subvendor = 0x1854, .pci_subdevice = 0x0068, .config = ALC880_LG }, - - { .modelname = "lg-lw", .config = ALC880_LG_LW }, - { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW }, - { .pci_subvendor = 0x1854, .pci_subdevice = 0x0077, .config = ALC880_LG_LW }, - +static const char *alc880_models[ALC880_MODEL_LAST] = { + [ALC880_3ST] = "3stack", + [ALC880_TCL_S700] = "tcl", + [ALC880_3ST_DIG] = "3stack-digout", + [ALC880_CLEVO] = "clevo", + [ALC880_5ST] = "5stack", + [ALC880_5ST_DIG] = "5stack-digout", + [ALC880_W810] = "w810", + [ALC880_Z71V] = "z71v", + [ALC880_6ST] = "6stack", + [ALC880_6ST_DIG] = "6stack-digout", + [ALC880_ASUS] = "asus", + [ALC880_ASUS_W1V] = "asus-w1v", + [ALC880_ASUS_DIG] = "asus-dig", + [ALC880_ASUS_DIG2] = "asus-dig2", + [ALC880_UNIWILL_DIG] = "uniwill", + [ALC880_F1734] = "F1734", + [ALC880_LG] = "lg", + [ALC880_LG_LW] = "lg-lw", #ifdef CONFIG_SND_DEBUG - { .modelname = "test", .config = ALC880_TEST }, + [ALC880_TEST] = "test", #endif - { .modelname = "auto", .config = ALC880_AUTO }, + [ALC880_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc880_cfg_tbl[] = { + /* Broken BIOS configuration */ + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), + + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), + + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST), + + SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), + SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V), + /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ + SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_5ST), + SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), + SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), + SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), + + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), + SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), + + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), + + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), + + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), + SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), + SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), + + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), {} }; @@ -3074,8 +3020,10 @@ static int patch_alc880(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl); - if (board_config < 0 || board_config >= ALC880_MODEL_LAST) { + board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST, + alc880_models, + alc880_cfg_tbl); + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for ALC880, " "trying auto-probe from BIOS...\n"); board_config = ALC880_AUTO; @@ -4161,33 +4109,32 @@ static void alc260_auto_init(struct hda_codec *codec) /* * ALC260 configurations */ -static struct hda_board_config alc260_cfg_tbl[] = { - { .modelname = "basic", .config = ALC260_BASIC }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb, - .config = ALC260_BASIC }, /* Sony VAIO */ - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cc, - .config = ALC260_BASIC }, /* Sony VAIO VGN-S3HP */ - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cd, - .config = ALC260_BASIC }, /* Sony VAIO */ - { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729, - .config = ALC260_BASIC }, /* CTL Travel Master U553W */ - { .modelname = "hp", .config = ALC260_HP }, - { .modelname = "hp-3013", .config = ALC260_HP_3013 }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP_3013 }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3016, .config = ALC260_HP }, - { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702X }, - { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702X }, - { .modelname = "acer", .config = ALC260_ACER }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x008f, .config = ALC260_ACER }, +static const char *alc260_models[ALC260_MODEL_LAST] = { + [ALC260_BASIC] = "basic", + [ALC260_HP] = "hp", + [ALC260_HP_3013] = "hp-3013", + [ALC260_FUJITSU_S702X] = "fujitsu", + [ALC260_ACER] = "acer", #ifdef CONFIG_SND_DEBUG - { .modelname = "test", .config = ALC260_TEST }, + [ALC260_TEST] = "test", #endif - { .modelname = "auto", .config = ALC260_AUTO }, + [ALC260_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc260_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), + SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), + SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), + SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), {} }; @@ -4286,8 +4233,10 @@ static int patch_alc260(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl); - if (board_config < 0 || board_config >= ALC260_MODEL_LAST) { + board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST, + alc260_models, + alc260_cfg_tbl); + if (board_config < 0) { snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, " "trying auto-probe from BIOS...\n"); board_config = ALC260_AUTO; @@ -4668,19 +4617,18 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = { /* * configuration and preset */ -static struct hda_board_config alc882_cfg_tbl[] = { - { .modelname = "3stack-dig", .config = ALC882_3ST_DIG }, - { .modelname = "6stack-dig", .config = ALC882_6ST_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, - .config = ALC882_6ST_DIG }, /* MSI */ - { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, - .config = ALC882_6ST_DIG }, /* Foxconn */ - { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, - .config = ALC882_6ST_DIG }, /* ECS to Intel*/ - { .modelname = "arima", .config = ALC882_ARIMA }, - { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, - .config = ALC882_ARIMA }, /* Arima W820Di1 */ - { .modelname = "auto", .config = ALC882_AUTO }, +static const char *alc882_models[ALC882_MODEL_LAST] = { + [ALC882_3ST_DIG] = "3stack-dig", + [ALC882_6ST_DIG] = "6stack-dig", + [ALC882_ARIMA] = "arima", + [ALC882_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc882_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), {} }; @@ -4817,7 +4765,9 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl); + board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, + alc882_models, + alc882_cfg_tbl); if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { printk(KERN_INFO "hda_codec: Unknown model for ALC882, " @@ -5427,65 +5377,41 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = { /* * configuration and preset */ -static struct hda_board_config alc883_cfg_tbl[] = { - { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG }, - { .modelname = "3stack-6ch-dig", .config = ALC883_3ST_6ch_DIG }, - { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, - .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ - { .modelname = "3stack-6ch", .config = ALC883_3ST_6ch }, - { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, - .config = ALC883_3ST_6ch }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601, - .config = ALC883_3ST_6ch }, /* D102GGC */ - { .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, - .config = ALC883_6ST_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x7280, - .config = ALC883_6ST_DIG }, /* MSI K9A Platinum (MS-7280) */ - { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, - .config = ALC883_6ST_DIG }, /* Foxconn */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x7187, - .config = ALC883_6ST_DIG }, /* MSI */ - { .modelname = "targa-dig", .config = ALC883_TARGA_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x4314, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fcc, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fc1, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x3fc3, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x4314, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x4319, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x3ef9, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x4324, - .config = ALC883_TARGA_DIG }, /* MSI */ - { .modelname = "targa-2ch-dig", .config = ALC883_TARGA_2ch_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x0579, - .config = ALC883_TARGA_2ch_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0xa422, - .config = ALC883_TARGA_2ch_DIG }, /* MSI */ - { .pci_subvendor = 0x1462, .pci_subdevice = 0x3b7f, - .config = ALC883_TARGA_2ch_DIG }, /* MSI */ - { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, - { .modelname = "acer", .config = ALC883_ACER }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/, - .config = ALC883_ACER }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x0102, - .config = ALC883_ACER }, - { .pci_subvendor = 0x1025, .pci_subdevice = 0x009f, - .config = ALC883_ACER }, - { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054, - .modelname = "medion", .config = ALC883_MEDION }, - { .modelname = "laptop-eapd", .config = ALC883_LAPTOP_EAPD }, - { .pci_subvendor = 0x1071, .pci_subdevice = 0x8258, - .config = ALC883_LAPTOP_EAPD }, /* Evesham Voyager C530RD */ - { .pci_subvendor = 0x1558, .pci_subdevice = 0, - .config = ALC883_LAPTOP_EAPD }, /* Clevo */ - { .modelname = "auto", .config = ALC883_AUTO }, +static const char *alc883_models[ALC883_MODEL_LAST] = { + [ALC883_3ST_2ch_DIG] = "3stack-dig", + [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", + [ALC883_3ST_6ch] = "3stack-6ch", + [ALC883_6ST_DIG] = "6stack-dig", + [ALC883_TARGA_DIG] = "targa-dig", + [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", + [ALC888_DEMO_BOARD] = "6stack-dig-demo", + [ALC883_ACER] = "acer", + [ALC883_MEDION] = "medion", + [ALC883_LAPTOP_EAPD] = "laptop-eapd", + [ALC883_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc883_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), + SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), {} }; @@ -5734,8 +5660,10 @@ static int patch_alc883(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl); - if (board_config < 0 || board_config >= ALC883_MODEL_LAST) { + board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST, + alc883_models, + alc883_cfg_tbl); + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for ALC883, " "trying auto-probe from BIOS...\n"); board_config = ALC883_AUTO; @@ -6438,35 +6366,27 @@ static void alc262_auto_init(struct hda_codec *codec) /* * configuration and preset */ -static struct hda_board_config alc262_cfg_tbl[] = { - { .modelname = "basic", .config = ALC262_BASIC }, - { .modelname = "hippo", - .pci_subvendor =0x1002, .pci_subdevice = 0x437b, - .config = ALC262_HIPPO}, - { .modelname = "hippo", - .pci_subvendor = 0x104d, .pci_subdevice = 0x8203, - .config = ALC262_HIPPO }, /* Sony UX-90s */ - { .modelname = "hippo_1", - .pci_subvendor =0x17ff, .pci_subdevice = 0x058f, - .config = ALC262_HIPPO_1}, - { .modelname = "fujitsu", .config = ALC262_FUJITSU }, - { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, - .config = ALC262_FUJITSU }, - { .modelname = "hp-bpc", .config = ALC262_HP_BPC }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x280c, - .config = ALC262_HP_BPC }, /* xw4400 */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x2801, - .config = ALC262_HP_BPC }, /* q965 */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, - .config = ALC262_HP_BPC }, /* xw6400 */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, - .config = ALC262_HP_BPC }, /* xw8400 */ - { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe, - .config = ALC262_HP_BPC }, /* xw9400 */ - { .modelname = "benq", .config = ALC262_BENQ_ED8 }, - { .pci_subvendor = 0x17ff, .pci_subdevice = 0x0560, - .config = ALC262_BENQ_ED8 }, - { .modelname = "auto", .config = ALC262_AUTO }, +static const char *alc262_models[ALC262_MODEL_LAST] = { + [ALC262_BASIC] = "basic", + [ALC262_HIPPO] = "hippo", + [ALC262_HIPPO_1] = "hippo_1", + [ALC262_FUJITSU] = "fujitsu", + [ALC262_HP_BPC] = "hp-bpc", + [ALC262_BENQ_ED8] = "benq", + [ALC262_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc262_cfg_tbl[] = { + SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), + SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x2801, "HP q954", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), + SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), {} }; @@ -6561,9 +6481,11 @@ static int patch_alc262(struct hda_codec *codec) } #endif - board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl); + board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, + alc262_models, + alc262_cfg_tbl); - if (board_config < 0 || board_config >= ALC262_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for ALC262, " "trying auto-probe from BIOS...\n"); board_config = ALC262_AUTO; @@ -7527,30 +7449,26 @@ static void alc861_auto_init(struct hda_codec *codec) /* * configuration and preset */ -static struct hda_board_config alc861_cfg_tbl[] = { - { .modelname = "3stack", .config = ALC861_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, - .config = ALC861_3ST }, - { .modelname = "3stack-660", .config = ALC660_3ST }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7, - .config = ALC660_3ST }, - { .modelname = "3stack-dig", .config = ALC861_3ST_DIG }, - { .modelname = "6stack-dig", .config = ALC861_6ST_DIG }, - { .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31}, - { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072, - .config = ALC861_UNIWILL_M31 }, - { .modelname = "toshiba", .config = ALC861_TOSHIBA }, - { .pci_subvendor = 0x1179, .pci_subdevice = 0xff10, - .config = ALC861_TOSHIBA }, - { .modelname = "asus", .config = ALC861_ASUS}, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1393, - .config = ALC861_ASUS }, - { .modelname = "asus-laptop", .config = ALC861_ASUS_LAPTOP }, - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1335, - .config = ALC861_ASUS_LAPTOP }, /* ASUS F2/F3 */ - { .pci_subvendor = 0x1043, .pci_subdevice = 0x1338, - .config = ALC861_ASUS_LAPTOP }, /* ASUS F2/F3 */ - { .modelname = "auto", .config = ALC861_AUTO }, +static const char *alc861_models[ALC861_MODEL_LAST] = { + [ALC861_3ST] = "3stack", + [ALC660_3ST] = "3stack-660", + [ALC861_3ST_DIG] = "3stack-dig", + [ALC861_6ST_DIG] = "6stack-dig", + [ALC861_UNIWILL_M31] = "uniwill-m31", + [ALC861_TOSHIBA] = "toshiba", + [ALC861_ASUS] = "asus", + [ALC861_ASUS_LAPTOP] = "asus-laptop", + [ALC861_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc861_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660_3ST), + SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), + SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), {} }; @@ -7673,9 +7591,11 @@ static int patch_alc861(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl); + board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST, + alc861_models, + alc861_cfg_tbl); - if (board_config < 0 || board_config >= ALC861_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for ALC861, " "trying auto-probe from BIOS...\n"); board_config = ALC861_AUTO; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c8696ddc03ac..cbaa00aa5b92 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -37,14 +37,30 @@ #define NUM_CONTROL_ALLOC 32 #define STAC_HP_EVENT 0x37 -#define STAC_REF 0 -#define STAC_D945GTP3 1 -#define STAC_D945GTP5 2 -#define STAC_MACMINI 3 -#define STAC_922X_MODELS 4 /* number of 922x models */ -#define STAC_D965_3ST 4 -#define STAC_D965_5ST 5 -#define STAC_927X_MODELS 6 /* number of 927x models */ +enum { + STAC_REF, + STAC_9200_MODELS +}; + +enum { + STAC_9205_REF, + STAC_9205_MODELS +}; + +enum { + STAC_D945_REF, + STAC_D945GTP3, + STAC_D945GTP5, + STAC_MACMINI, + STAC_922X_MODELS +}; + +enum { + STAC_D965_REF, + STAC_D965_3ST, + STAC_D965_5ST, + STAC_927X_MODELS +}; struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; @@ -373,22 +389,25 @@ static unsigned int ref9200_pin_configs[8] = { 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, }; -static unsigned int *stac9200_brd_tbl[] = { - ref9200_pin_configs, +static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { + [STAC_REF] = ref9200_pin_configs, }; -static struct hda_board_config stac9200_cfg_tbl[] = { - { .modelname = "ref", - .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2668, /* DFI LanParty */ - .config = STAC_REF }, +static const char *stac9200_models[STAC_9200_MODELS] = { + [STAC_REF] = "ref", +}; + +static struct snd_pci_quirk stac9200_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_REF), /* Dell laptops have BIOS problem */ - { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01b5, - .config = STAC_REF }, /* Dell Inspiron 630m */ - { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01c2, - .config = STAC_REF }, /* Dell Latitude D620 */ - { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01cb, - .config = STAC_REF }, /* Dell Latitude 120L */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5, + "Dell Inspiron 630m", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2, + "Dell Latitude D620", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb, + "Dell Latitude 120L", STAC_REF), {} /* terminator */ }; @@ -411,100 +430,80 @@ static unsigned int d945gtp5_pin_configs[10] = { }; static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { - [STAC_REF] = ref922x_pin_configs, + [STAC_D945_REF] = ref922x_pin_configs, [STAC_D945GTP3] = d945gtp3_pin_configs, [STAC_D945GTP5] = d945gtp5_pin_configs, [STAC_MACMINI] = d945gtp5_pin_configs, }; -static struct hda_board_config stac922x_cfg_tbl[] = { - { .modelname = "5stack", .config = STAC_D945GTP5 }, - { .modelname = "3stack", .config = STAC_D945GTP3 }, - { .modelname = "ref", - .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2668, /* DFI LanParty */ - .config = STAC_REF }, /* SigmaTel reference board */ - /* Intel 945G based systems */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0101, - .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0202, - .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0606, - .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0601, - .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0111, - .config = STAC_D945GTP3 }, /* Intel D945GZP - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x1115, - .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x1116, - .config = STAC_D945GTP3 }, /* Intel D945GBO - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x1117, - .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x1118, - .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x1119, - .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x8826, - .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x5049, - .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x5055, - .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x5048, - .config = STAC_D945GTP3 }, /* Intel D945GPB - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0110, - .config = STAC_D945GTP3 }, /* Intel D945GLR - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0404, - .config = STAC_D945GTP5 }, /* Intel D945GTP - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0303, - .config = STAC_D945GTP5 }, /* Intel D945GNT - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0013, - .config = STAC_D945GTP5 }, /* Intel D955XBK - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0417, - .config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */ - /* Intel 945P based systems */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0b0b, - .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0112, - .config = STAC_D945GTP3 }, /* Intel D945PLN - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0d0d, - .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0909, - .config = STAC_D945GTP3 }, /* Intel D945PAW - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0505, - .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x0707, - .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */ - /* other systems */ - { .pci_subvendor = 0x8384, - .pci_subdevice = 0x7680, - .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */ +static const char *stac922x_models[STAC_922X_MODELS] = { + [STAC_D945_REF] = "ref", + [STAC_D945GTP5] = "5stack", + [STAC_D945GTP3] = "3stack", + [STAC_MACMINI] = "macmini", +}; + +static struct snd_pci_quirk stac922x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_D945_REF), + /* Intel 945G based systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048, + "Intel D945G", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110, + "Intel D945G", STAC_D945GTP3), + /* Intel D945G 5-stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404, + "Intel D945G", STAC_D945GTP5), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303, + "Intel D945G", STAC_D945GTP5), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013, + "Intel D945G", STAC_D945GTP5), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417, + "Intel D945G", STAC_D945GTP5), + /* Intel 945P based systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b, + "Intel D945P", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112, + "Intel D945P", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d, + "Intel D945P", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909, + "Intel D945P", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505, + "Intel D945P", STAC_D945GTP3), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707, + "Intel D945P", STAC_D945GTP5), + /* other systems */ + /* Apple Mac Mini (early 2006) */ + SND_PCI_QUIRK(0x8384, 0x7680, + "Mac Mini", STAC_MACMINI), {} /* terminator */ }; @@ -530,102 +529,51 @@ static unsigned int d965_5st_pin_configs[14] = { }; static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { - [STAC_REF] = ref927x_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, [STAC_D965_3ST] = d965_3st_pin_configs, [STAC_D965_5ST] = d965_5st_pin_configs, }; -static struct hda_board_config stac927x_cfg_tbl[] = { - { .modelname = "5stack", .config = STAC_D965_5ST }, - { .modelname = "3stack", .config = STAC_D965_3ST }, - { .modelname = "ref", - .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2668, /* DFI LanParty */ - .config = STAC_REF }, /* SigmaTel reference board */ +static const char *stac927x_models[STAC_927X_MODELS] = { + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", +}; + +static struct snd_pci_quirk stac927x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_D965_REF), /* Intel 946 based systems */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x3d01, - .config = STAC_D965_3ST }, /* D946 configuration */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0xa301, - .config = STAC_D965_3ST }, /* Intel D946GZT - 3 stack */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST), /* 965 based 3 stack systems */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2116, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2115, - .config = STAC_D965_3ST }, /* Intel DQ965WC - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2114, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2113, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2112, - .config = STAC_D965_3ST }, /* Intel DG965MS - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2111, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2110, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2009, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2008, - .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2007, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2006, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2005, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2004, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2003, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2002, - .config = STAC_D965_3ST }, /* Intel D965 3Stack config */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2001, - .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), /* 965 based 5 stack systems */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2301, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2302, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2303, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2304, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2305, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2501, - .config = STAC_D965_5ST }, /* Intel DG965MQ - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2502, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2503, - .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */ - { .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2504, - .config = STAC_D965_5ST }, /* Intel DQ965GF - 5 Stack */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST), {} /* terminator */ }; @@ -635,15 +583,18 @@ static unsigned int ref9205_pin_configs[12] = { 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; -static unsigned int *stac9205_brd_tbl[] = { +static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { ref9205_pin_configs, }; -static struct hda_board_config stac9205_cfg_tbl[] = { - { .modelname = "ref", - .pci_subvendor = PCI_VENDOR_ID_INTEL, - .pci_subdevice = 0x2668, /* DFI LanParty */ - .config = STAC_REF }, /* SigmaTel reference board */ +static const char *stac9205_models[STAC_9205_MODELS] = { + [STAC_9205_REF] = "ref", +}; + +static struct snd_pci_quirk stac9205_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_9205_REF), {} /* terminator */ }; @@ -1710,7 +1661,9 @@ static int patch_stac9200(struct hda_codec *codec) codec->spec = spec; spec->num_pins = 8; spec->pin_nids = stac9200_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, stac9200_cfg_tbl); + spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, + stac9200_models, + stac9200_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); @@ -1758,7 +1711,9 @@ static int patch_stac922x(struct hda_codec *codec) codec->spec = spec; spec->num_pins = 10; spec->pin_nids = stac922x_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl); + spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, + stac922x_models, + stac922x_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " "using BIOS defaults\n"); @@ -1809,7 +1764,9 @@ static int patch_stac927x(struct hda_codec *codec) codec->spec = spec; spec->num_pins = 14; spec->pin_nids = stac927x_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, stac927x_cfg_tbl); + spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, + stac927x_models, + stac927x_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); @@ -1874,7 +1831,9 @@ static int patch_stac9205(struct hda_codec *codec) codec->spec = spec; spec->num_pins = 14; spec->pin_nids = stac9205_pin_nids; - spec->board_config = snd_hda_check_board_config(codec, stac9205_cfg_tbl); + spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, + stac9205_models, + stac9205_cfg_tbl); if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); @@ -2083,18 +2042,19 @@ enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */ /* Unknown. id=0x83847661 and subsys=0x104D1200. */ STAC9872K_VAIO, /* AR Series. id=0x83847664 and subsys=104D1300 */ - CXD9872AKD_VAIO - }; - -static struct hda_board_config stac9872_cfg_tbl[] = { - { .modelname = "vaio", .config = CXD9872RD_VAIO }, - { .modelname = "vaio-ar", .config = CXD9872AKD_VAIO }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81e6, - .config = CXD9872RD_VAIO }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81ef, - .config = CXD9872RD_VAIO }, - { .pci_subvendor = 0x104d, .pci_subdevice = 0x81fd, - .config = CXD9872AKD_VAIO }, + CXD9872AKD_VAIO, + STAC_9872_MODELS, +}; + +static const char *stac9872_models[STAC_9872_MODELS] = { + [CXD9872RD_VAIO] = "vaio", + [CXD9872AKD_VAIO] = "vaio-ar", +}; + +static struct snd_pci_quirk stac9872_cfg_tbl[] = { + SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO), + SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO), + SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO), {} }; @@ -2103,7 +2063,9 @@ static int patch_stac9872(struct hda_codec *codec) struct sigmatel_spec *spec; int board_config; - board_config = snd_hda_check_board_config(codec, stac9872_cfg_tbl); + board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, + stac9872_models, + stac9872_cfg_tbl); if (board_config < 0) /* unknown config, let generic-parser do its job... */ return snd_hda_parse_generic_codec(codec); -- cgit v1.2.3-59-g8ed1b From 2e26e483694059d63bda7bb89d5a464c952d1d44 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 27 Nov 2006 12:05:04 +0100 Subject: [ALSA] ASoC - Bit clock matching error This patch by Philipp Zabel fixes a bug whereby the BCLK matching fails when the Codec BCLK is constant and the CPU BCLK is based upon a divider. Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6da1616bf776..90e8841e7e33 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -424,7 +424,7 @@ static int soc_hw_match_params(struct snd_pcm_substream *substream, /* normalise cpu bfs div & codec const mult */ codec_bfs = soc_bfs_rate_to_div(codec_dai_mode->bfs, rate, mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - if(codec_dai_mode->bfs & codec_bfs) { + if(cpu_dai_mode->bfs & codec_bfs) { rtd->cpu_dai->dai_runtime.bfs = codec_bfs; rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs; } else -- cgit v1.2.3-59-g8ed1b From 86d72bdfcd34c9cd8acddf749ff130d5365fe279 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Nov 2006 11:33:10 +0100 Subject: [ALSA] hda-codec - Fix compile warnings without CONFIG_SND_DEBUG Fix compile warnings (unused variables) in patch_conexant.c without CONFIG_SND_DEBUG. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_conexant.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index dec8f9747fc6..73f4668238c6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -397,6 +397,9 @@ static int conexant_eapd_put(struct snd_kcontrol *kcontrol, return 1; } +/* controls for test mode */ +#ifdef CONFIG_SND_DEBUG + static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -545,6 +548,8 @@ static int cxt_spdif_ctrl_put(struct snd_kcontrol *kcontrol, .put = cxt_spdif_ctrl_put, \ .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + /* Conexant 5045 specific */ static hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; -- cgit v1.2.3-59-g8ed1b From bd869485993f73c303b565da5548bb4e77063c54 Mon Sep 17 00:00:00 2001 From: Jonathan Woithe Date: Tue, 28 Nov 2006 11:35:52 +0100 Subject: [ALSA] hda-codec - Make internal speaker work on Acer C20x tablets The following patch creates a new 'Mono speaker' control in alsamixer when the Realtek 'acer' model is used with hda_intel. This is needed so the internal mono speaker (when present) can be controlled. This new control won't do anything in Acer laptops which are not fitted with a mono speaker. Acer models which are known to have a mono speaker are the C20x tablet series but there may be others. I guess we could define a new model specifically for Acers with mono speakers but this seems a bit silly given that such a model will be identical to the normal 'acer' model except for this added control. This patch also adds the C20x tablets to the list of PCI ids associated with the 'acer' model. This means that owners of C20x machines will no longer have to supply 'model=acer' when loading hda_intel. Signed-off-by: Jonathan Woithe Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 415a6db4c909..a1b6c9661d45 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3271,11 +3271,20 @@ static struct snd_kcontrol_new alc260_fujitsu_mixer[] = { * and the output jack. If this turns out to be the case for all such * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT * to ALC_PIN_DIR_INOUT_NOMICBIAS. + * + * The C20x Tablet series have a mono internal speaker which is controlled + * via the chip's Mono sum widget and pin complex, so include the necessary + * controls for such models. On models without a "mono speaker" the control + * won't do anything. */ static struct snd_kcontrol_new alc260_acer_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME_MONO("Mono Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono Speaker Playback Switch", 0x0a, 1, 2, + HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), @@ -3590,11 +3599,11 @@ static struct hda_verb alc260_acer_init_verbs[] = { {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, /* Line In jack is connected to Line1 pin */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, /* Ensure all other unused pins are disabled and muted. */ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, @@ -3622,6 +3631,8 @@ static struct hda_verb alc260_acer_init_verbs[] = { /* Unmute Line-out pin widget amp left and right (no equiv mixer ctrl) */ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute mono pin widget amp output (no equiv mixer ctrl) */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Unmute Mic1 and Line1 pin widget input buffers since they start as * inputs. If the pin mode is changed by the user the pin mode control * will take care of enabling the pin's input/output buffers as needed. @@ -4122,6 +4133,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = { }; static struct snd_pci_quirk alc260_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP), -- cgit v1.2.3-59-g8ed1b From d9c96cf35b70b484483446c92f27652f3aef977e Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 28 Nov 2006 12:10:09 +0100 Subject: [ALSA] sound/soc/soc-dapm.c: make 4 functions static Make the following needlessly global functions static: - dapm_power_widgets() - dapm_mux_update_power() - dapm_mixer_update_power() - dapm_free_widgets() Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-dapm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2c2c27f4e9c0..411651dc9d1d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -463,7 +463,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -int dapm_power_widgets(struct snd_soc_codec *codec, int event) +static int dapm_power_widgets(struct snd_soc_codec *codec, int event) { struct snd_soc_dapm_widget *w; int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power; @@ -664,8 +664,9 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) #endif /* test and update the power status of a mux widget */ -int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mask, int val, struct soc_enum* e) +static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int mask, + int val, struct soc_enum* e) { struct snd_soc_dapm_path *path; int found = 0; @@ -697,11 +698,11 @@ int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; } -EXPORT_SYMBOL_GPL(dapm_mux_update_power); /* test and update the power status of a mixer widget */ -int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert) +static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int reg, + int val_mask, int val, int invert) { struct snd_soc_dapm_path *path; int found = 0; @@ -733,7 +734,6 @@ int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, return 0; } -EXPORT_SYMBOL_GPL(dapm_mixer_update_power); /* show dapm widget status in sys fs */ static ssize_t dapm_widget_show(struct device *dev, @@ -808,7 +808,7 @@ static void snd_soc_dapm_sys_remove(struct device *dev) } /* free all dapm widgets and resources */ -void dapm_free_widgets(struct snd_soc_codec *codec) +static void dapm_free_widgets(struct snd_soc_codec *codec) { struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p; -- cgit v1.2.3-59-g8ed1b From 6c5cfd9d9d312b279adf9226b7e664f6f4c4cfc7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Nov 2006 17:18:25 +0100 Subject: [ALSA] Add description about spdif_aclink option for snd-intel8x0 Added a description about spdif_aclink option for snd-intel8x0 driver in ALSA-Configuration.txt. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 15d102a94619..89b612f835ad 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1079,6 +1079,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. buggy_semaphore - Enable workaround for hardwares with buggy semaphores (e.g. on some ASUS laptops) (default off) + spdif_aclink - Use S/PDIF over AC-link instead of direct connection + from the controller chip + (0 = off, 1 = on, -1 = default) This module supports one chip and autoprobe. -- cgit v1.2.3-59-g8ed1b From c577b8a16fd19a33a8865ca6451287d284a0faf6 Mon Sep 17 00:00:00 2001 From: Joseph Chan Date: Wed, 29 Nov 2006 15:29:40 +0100 Subject: [ALSA] hda-codec - Add support for VIA VT1708(A) HD audio codec This patch is VIA first release for HD audio codec, VT1708(A) and it provides geneneral HD audio driver features. Signed-off-by: Joseph Chan Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/Makefile | 3 +- sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_patch.h | 3 + sound/pci/hda/patch_via.c | 1396 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1402 insertions(+), 1 deletion(-) create mode 100644 sound/pci/hda/patch_via.c diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 148140bb86bd..60d7b05a204a 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -7,7 +7,8 @@ snd-hda-codec-objs := hda_codec.o \ patch_sigmatel.o \ patch_si3054.o \ patch_atihdmi.o \ - patch_conexant.o + patch_conexant.o \ + patch_via.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c07d5db6b050..e14faf5d5053 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -52,6 +52,7 @@ struct hda_vendor_id { static struct hda_vendor_id hda_vendor_ids[] = { { 0x10ec, "Realtek" }, { 0x1057, "Motorola" }, + { 0x1106, "VIA" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index 5904ecd88a50..9f9e9ae44a9d 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -16,6 +16,8 @@ extern struct hda_codec_preset snd_hda_preset_si3054[]; extern struct hda_codec_preset snd_hda_preset_atihdmi[]; /* Conexant audio codec */ extern struct hda_codec_preset snd_hda_preset_conexant[]; +/* VIA codecs */ +extern struct hda_codec_preset snd_hda_preset_via[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, @@ -25,5 +27,6 @@ static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_si3054, snd_hda_preset_atihdmi, snd_hda_preset_conexant, + snd_hda_preset_via, NULL }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c new file mode 100644 index 000000000000..4c839b031729 --- /dev/null +++ b/sound/pci/hda/patch_via.c @@ -0,0 +1,1396 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for VIA VT1708 codec + * + * Copyright (c) 2006 Lydia Wang + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* * * * * * * * * * * * * * 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-09-08 Lydia Wang Fix internal loopback recording source select bug */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + + +/* amp values */ +#define AMP_VAL_IDX_SHIFT 19 +#define AMP_VAL_IDX_MASK (0x0f<<19) + +#define NUM_CONTROL_ALLOC 32 +#define NUM_VERB_ALLOC 32 + +/* Pin Widget NID */ +#define VT1708_HP_NID 0x13 +#define VT1708_DIGOUT_NID 0x14 +#define VT1708_DIGIN_NID 0x16 + +#define VT1709_HP_DAC_NID 0x28 +#define VT1709_DIGOUT_NID 0x13 +#define VT1709_DIGIN_NID 0x17 + +#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) + + +enum { + VIA_CTL_WIDGET_VOL, + VIA_CTL_WIDGET_MUTE, +}; + +enum { + AUTO_SEQ_FRONT, + AUTO_SEQ_SURROUND, + AUTO_SEQ_CENLFE, + AUTO_SEQ_SIDE +}; + +static struct snd_kcontrol_new vt1708_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), +}; + + +struct via_spec { + /* codec parameterization */ + struct snd_kcontrol_new *mixers[3]; + unsigned int num_mixers; + + struct hda_verb *init_verbs; + + 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; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t dig_in_nid; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + + /* PCM information */ + struct hda_pcm pcm_rec[2]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + unsigned int num_kctl_alloc, num_kctl_used; + struct snd_kcontrol_new *kctl_alloc; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[4]; +}; + +static hda_nid_t vt1708_adc_nids[2] = { + /* ADC1-2 */ + 0x15, 0x27 +}; + +static hda_nid_t vt1709_adc_nids[3] = { + /* ADC1-2 */ + 0x14, 0x15, 0x16 +}; + +/* add dynamic controls */ +static int via_add_control(struct via_spec *spec, int type, const char *name, + unsigned long val) +{ + struct snd_kcontrol_new *knew; + + if (spec->num_kctl_used >= spec->num_kctl_alloc) { + int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; + + /* array + terminator */ + knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); + if (!knew) + return -ENOMEM; + if (spec->kctl_alloc) { + memcpy(knew, spec->kctl_alloc, + sizeof(*knew) * spec->num_kctl_alloc); + kfree(spec->kctl_alloc); + } + spec->kctl_alloc = knew; + spec->num_kctl_alloc = num; + } + + knew = &spec->kctl_alloc[spec->num_kctl_used]; + *knew = vt1708_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + + if (!knew->name) + return -ENOMEM; + knew->private_value = val; + spec->num_kctl_used++; + return 0; +} + +/* 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) +{ + char name[32]; + int err; + + sprintf(name, "%s Playback Volume", ctlname); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + return 0; +} + +static void via_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); +} + + +static void via_auto_init_multi_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} + +static void via_auto_init_hp_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pins[0]; + 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) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + (i <= AUTO_PIN_FRONT_MIC ? + PIN_VREF50 : PIN_IN)); + + } +} +/* + * input MUX handling + */ +static int via_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} + +static int via_mux_enum_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; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int via_mux_enum_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; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + unsigned int vendor_id = codec->vendor_id; + + /* AIW0 lydia 060801 add for correct sw0 input select */ + if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0)) + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + 0x18, &spec->cur_mux[adc_idx]); + else if ((IS_VT1709_10CH_VENDORID(vendor_id) || + IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) ) + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + 0x19, &spec->cur_mux[adc_idx]); + else + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + spec->adc_nids[adc_idx], + &spec->cur_mux[adc_idx]); +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1708_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb vt1708_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x19 - 0x1b) + */ + /* set vol=0 to output mixers */ + {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 to PW4 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Set mic as default input of sw0 */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x2}, + /* PW9 Output enable */ + {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +}; + +static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); +} + +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; + 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; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int via_capture_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; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + 0, 0, 0); + return 0; +} + +static struct hda_pcm_stream vt1708_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x15, /* NID to query formats and rates */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream vt1708_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int via_build_controls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + int i; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); + if (err < 0) + return err; + } + return 0; +} + +static int via_build_pcms(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + 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_CAPTURE] = *(spec->stream_analog_capture); + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms++; + info++; + info->name = spec->stream_name_digital; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *(spec->stream_digital_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *(spec->stream_digital_capture); + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->dig_in_nid; + } + } + + return 0; +} + +static void via_free(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + unsigned int i; + + if (!spec) + return; + + if (spec->kctl_alloc) { + for (i = 0; i < spec->num_kctl_used; i++) + kfree(spec->kctl_alloc[i].name); + kfree(spec->kctl_alloc); + } + + kfree(codec->spec); +} + +static int via_init(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + snd_hda_sequence_write(codec, spec->init_verbs); + return 0; +} + +#ifdef CONFIG_PM +/* + * resume + */ +static int via_resume(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + via_init(codec); + for (i = 0; i < spec->num_mixers; i++) + snd_hda_resume_ctls(codec, spec->mixers[i]); + if (spec->multiout.dig_out_nid) + snd_hda_resume_spdif_out(codec); + if (spec->dig_in_nid) + snd_hda_resume_spdif_in(codec); + + return 0; +} +#endif + +/* + */ +static struct hda_codec_ops via_patch_ops = { + .build_controls = via_build_controls, + .build_pcms = via_build_pcms, + .init = via_init, + .free = via_free, +#ifdef CONFIG_PM + .resume = via_resume, +#endif +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1708_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] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x12; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x13; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1708_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, nid_vol = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + if (i != AUTO_SEQ_FRONT) + nid_vol = 0x1b - i + 1; + + 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_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(nid_vol, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } 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", + HDA_COMPOSE_AMP_VAL(0x17, 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_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, + HDA_COMPOSE_AMP_VAL(nid, 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, 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_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + } + + return 0; +} + +static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(pin, 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; + + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1708_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; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = idx; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1d: /* Mic */ + idx = 2; + break; + + case 0x1e: /* Line In */ + idx = 3; + break; + + case 0x21: /* Front Mic */ + idx = 4; + break; + + case 0x24: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], + idx, 0x17); + 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 vt1708_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 = vt1708_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 = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1708_DIGIN_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs = vt1708_volume_init_verbs; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +/* init callback for auto-configuration model -- overriding the default init */ +static int via_auto_init(struct hda_codec *codec) +{ + via_init(codec); + via_auto_init_multi_out(codec); + via_auto_init_hp_out(codec); + via_auto_init_analog_input(codec); + return 0; +} + +static int patch_vt1708(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1708_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->stream_name_analog = "VT1708 Analog"; + spec->stream_analog_playback = &vt1708_pcm_analog_playback; + spec->stream_analog_capture = &vt1708_pcm_analog_capture; + + spec->stream_name_digital = "VT1708 Digital"; + 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); + spec->mixers[spec->num_mixers] = vt1708_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + + return 0; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1709_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb vt1709_10ch_volume_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output selector (0x1a, 0x1b, 0x29) + */ + /* set vol=0 to output mixers */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* + * Unmute PW3 and PW4 + */ + {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 mic as default input of sw0 */ + {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, + /* PW9 Output enable */ + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + { } +}; + +static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 10, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 6, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1709_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x14, /* NID to query formats and rates */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1709_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close + }, +}; + +static struct hda_pcm_stream vt1709_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int vt1709_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + if (cfg->line_outs == 4) /* 10 channels */ + spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ + else if (cfg->line_outs == 3) /* 6 channels */ + spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ + + spec->multiout.dac_nids = spec->private_dac_nids; + + if (cfg->line_outs == 4) { /* 10 channels */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + /* AOW0 */ + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + /* AOW2 */ + spec->multiout.dac_nids[i] = 0x12; + break; + case AUTO_SEQ_SURROUND: + /* AOW3 */ + spec->multiout.dac_nids[i] = 0x27; + break; + case AUTO_SEQ_SIDE: + /* AOW1 */ + spec->multiout.dac_nids[i] = 0x11; + break; + default: + break; + } + } + } + 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++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch(i) { + case AUTO_SEQ_FRONT: + /* AOW0 */ + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + /* AOW2 */ + spec->multiout.dac_nids[i] = 0x12; + break; + case AUTO_SEQ_SURROUND: + /* AOW1 */ + spec->multiout.dac_nids[i] = 0x11; + break; + default: + break; + } + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1709_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 = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + 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_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_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_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_OUTPUT)); + if (err < 0) + return err; + } 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", + HDA_COMPOSE_AMP_VAL(0x18, 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_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, + HDA_COMPOSE_AMP_VAL(nid, 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, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } 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(0x29, 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_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(0x1a, 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_OUTPUT)); + if (err < 0) + return err; + } + } + + return 0; +} + +static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + if (spec->multiout.num_dacs == 5) /* 10 channels */ + spec->multiout.hp_nid = VT1709_HP_DAC_NID; + else if (spec->multiout.num_dacs == 3) /* 6 channels */ + spec->multiout.hp_nid = 0; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(pin, 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; + + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1709_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; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = idx; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1d: /* Mic */ + idx = 2; + break; + + case 0x1e: /* Line In */ + idx = 3; + break; + + case 0x21: /* Front Mic */ + idx = 4; + break; + + case 0x23: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], + idx, 0x18); + 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 vt1709_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 = vt1709_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 = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1709_DIGIN_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +static int patch_vt1709_10ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + err = vt1709_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration. " + "Using genenic mode...\n"); + } + + spec->init_verbs = vt1709_10ch_volume_init_verbs; + + spec->stream_name_analog = "VT1709 Analog"; + spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1709_pcm_analog_capture; + + spec->stream_name_digital = "VT1709 Digital"; + 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); + spec->mixers[spec->num_mixers] = vt1709_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + + return 0; +} +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb vt1709_6ch_volume_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output selector (0x1a, 0x1b, 0x29) + */ + /* set vol=0 to output mixers */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* + * Unmute PW3 and PW4 + */ + {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 MW0 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, + /* Set mic as default input of sw0 */ + {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, + /* PW9 Output enable */ + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + { } +}; + +static int patch_vt1709_6ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + err = vt1709_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration. " + "Using genenic mode...\n"); + } + + spec->init_verbs = vt1709_6ch_volume_init_verbs; + + spec->stream_name_analog = "VT1709 Analog"; + spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1709_pcm_analog_capture; + + spec->stream_name_digital = "VT1709 Digital"; + 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); + spec->mixers[spec->num_mixers] = vt1709_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_via[] = { + { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708}, + { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, + { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, + { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, + { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, + { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, + { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, + { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, + { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + {} /* terminator */ +}; -- cgit v1.2.3-59-g8ed1b From 9ed1261e3e617d99b0eb74041d0337ff664e4f5b Mon Sep 17 00:00:00 2001 From: Teru KAMOGASHIRA Date: Mon, 4 Dec 2006 18:03:53 +0100 Subject: [ALSA] Current driver does not utilize 44.1kHz high quality sampling rate converter. Following patch will make the driver to use the 44.1kHz SRC automatically if the pcm source is 44.1kHz signed 16bit stereo. The SRC is available in YMF754 only. Signed-off-by: Teru KAMOGASHIRA Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- include/sound/ymfpci.h | 4 +++- sound/pci/ymfpci/ymfpci_main.c | 44 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h index eef46faaee30..203d2b45b788 100644 --- a/include/sound/ymfpci.h +++ b/include/sound/ymfpci.h @@ -270,6 +270,7 @@ struct snd_ymfpci_pcm { struct snd_pcm_substream *substream; struct snd_ymfpci_voice *voices[2]; /* playback only */ unsigned int running: 1, + use_441_slot: 1, output_front: 1, output_rear: 1, swap_rear: 1; @@ -324,6 +325,7 @@ struct snd_ymfpci { u32 active_bank; struct snd_ymfpci_voice voices[64]; + int src441_used; struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97; @@ -346,7 +348,7 @@ struct snd_ymfpci { int mode_dup4ch; int rear_opened; int spdif_opened; - struct { + struct snd_ymfpci_pcm_mixer { u16 left; u16 right; struct snd_kcontrol *ctl; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 5bde816cd5c4..8b076932f4f5 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -171,6 +171,17 @@ static u32 snd_ymfpci_calc_lpfQ(u32 rate) return val[0]; } +static void snd_ymfpci_pcm_441_volume_set(struct snd_ymfpci_pcm *ypcm) +{ + unsigned int value; + struct snd_ymfpci_pcm_mixer *mixer; + + mixer = &ypcm->chip->pcm_mixer[ypcm->substream->number]; + value = min_t(unsigned int, mixer->left, 0x7fff) >> 1; + value |= (min_t(unsigned int, mixer->right, 0x7fff) >> 1) << 16; + snd_ymfpci_writel(ypcm->chip, YDSXGR_BUF441OUTVOL, value); +} + /* * Hardware start management */ @@ -282,6 +293,10 @@ static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voic snd_assert(pvoice != NULL, return -EINVAL); snd_ymfpci_hw_stop(chip); spin_lock_irqsave(&chip->voice_lock, flags); + if (pvoice->number == chip->src441_used) { + chip->src441_used = -1; + pvoice->ypcm->use_441_slot = 0; + } pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; pvoice->ypcm = NULL; pvoice->interrupt = NULL; @@ -386,7 +401,7 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr); - if (ypcm->voices[1] != NULL) + if (ypcm->voices[1] != NULL && !ypcm->use_441_slot) chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr); ypcm->running = 1; break; @@ -394,7 +409,7 @@ static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; - if (ypcm->voices[1] != NULL) + if (ypcm->voices[1] != NULL && !ypcm->use_441_slot) chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0; ypcm->running = 0; break; @@ -481,6 +496,7 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int unsigned int nbank; u32 vol_left, vol_right; u8 use_left, use_right; + unsigned long flags; snd_assert(voice != NULL, return); if (runtime->channels == 1) { @@ -499,11 +515,27 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int vol_left = cpu_to_le32(0x40000000); vol_right = cpu_to_le32(0x40000000); } + spin_lock_irqsave(&ypcm->chip->voice_lock, flags); format = runtime->channels == 2 ? 0x00010000 : 0; if (snd_pcm_format_width(runtime->format) == 8) format |= 0x80000000; + else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 && + runtime->rate == 44100 && runtime->channels == 2 && + voiceidx == 0 && (ypcm->chip->src441_used == -1 || + ypcm->chip->src441_used == voice->number)) { + ypcm->chip->src441_used = voice->number; + ypcm->use_441_slot = 1; + format |= 0x10000000; + snd_ymfpci_pcm_441_volume_set(ypcm); + } + if (ypcm->chip->src441_used == voice->number && + (format & 0x10000000) == 0) { + ypcm->chip->src441_used = -1; + ypcm->use_441_slot = 0; + } if (runtime->channels == 2 && (voiceidx & 1) != 0) format |= 1; + spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags); for (nbank = 0; nbank < 2; nbank++) { bank = &voice->bank[nbank]; memset(bank, 0, sizeof(*bank)); @@ -1714,7 +1746,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol, spin_lock_irqsave(&chip->voice_lock, flags); if (substream->runtime && substream->runtime->private_data) { struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; - ypcm->update_pcm_vol = 2; + if (!ypcm->use_441_slot) + ypcm->update_pcm_vol = 2; + else + snd_ymfpci_pcm_441_volume_set(ypcm); } spin_unlock_irqrestore(&chip->voice_lock, flags); return 1; @@ -2253,7 +2288,7 @@ static int saved_regs_index[] = { YDSXGR_PRIADCLOOPVOL, YDSXGR_NATIVEDACINVOL, YDSXGR_NATIVEDACOUTVOL, - // YDSXGR_BUF441OUTVOL, + YDSXGR_BUF441OUTVOL, YDSXGR_NATIVEADCINVOL, YDSXGR_SPDIFLOOPVOL, YDSXGR_SPDIFOUTVOL, @@ -2368,6 +2403,7 @@ int __devinit snd_ymfpci_create(struct snd_card *card, chip->reg_area_phys = pci_resource_start(pci, 0); chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000); pci_set_master(pci); + chip->src441_used = -1; if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) { snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); -- cgit v1.2.3-59-g8ed1b From 184c1e2c4c4221c2b8d1e16c33314595373fa73f Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Wed, 6 Dec 2006 15:58:02 +0000 Subject: [ALSA] emu10k1: Add Audio capture support for Audigy 2 ZS Notebook. Implement functionallity in order to fixe ALSA bug#2058. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 3 + sound/pci/emu10k1/emu10k1_main.c | 70 ++++++++++++-- sound/pci/emu10k1/emumixer.c | 201 ++++++++++++++++++++++++++++++++++++++- sound/pci/emu10k1/io.c | 59 ++++++++++++ sound/pci/emu10k1/p17v.h | 47 +++++++++ 5 files changed, 373 insertions(+), 7 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 8b28304f2c42..32ce4bd31b46 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1427,6 +1427,8 @@ struct snd_emu10k1 { spinlock_t memblk_lock; unsigned int spdif_bits[3]; /* s/pdif out setup */ + unsigned int i2c_capture_source; + u8 i2c_capture_volume[4][2]; struct snd_emu10k1_fx8010 fx8010; /* FX8010 info */ int gpr_base; @@ -1532,6 +1534,7 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn); void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data); int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data); +int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value); int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value); int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value); int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 711e819e4a0b..80aa585eade4 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -46,6 +46,7 @@ #include #include "p16v.h" #include "tina2.h" +#include "p17v.h" /************************************************************************* @@ -120,11 +121,28 @@ static unsigned int spi_dac_init[] = { 0x0622, 0x1400, }; + +static unsigned int i2c_adc_init[][2] = { + { 0x17, 0x00 }, /* Reset */ + { 0x07, 0x00 }, /* Timeout */ + { 0x0b, 0x22 }, /* Interface control */ + { 0x0c, 0x22 }, /* Master mode control */ + { 0x0d, 0x08 }, /* Powerdown control */ + { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ + { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ + { 0x10, 0x7b }, /* ALC Control 1 */ + { 0x11, 0x00 }, /* ALC Control 2 */ + { 0x12, 0x32 }, /* ALC Control 3 */ + { 0x13, 0x00 }, /* Noise gate control */ + { 0x14, 0xa6 }, /* Limiter control */ + { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */ +}; static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) { unsigned int silent_page; int ch; + u32 tmp; /* disable audio and lock cache */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, @@ -163,8 +181,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Hacks for Alice3 to work independent of haP16V driver */ - u32 tmp; - //Setup SRCMulti_I2S SamplingRate tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp &= 0xfffff1ff; @@ -184,8 +200,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */ /* Hacks for Alice3 to work independent of haP16V driver */ - u32 tmp; - snd_printk(KERN_INFO "Audigy2 value: Special config.\n"); //Setup SRCMulti_I2S SamplingRate tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); @@ -231,6 +245,23 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ } + if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ + int size, n; + + snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f); + tmp = inl(emu->port + A_IOCFG); + outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ + tmp = inl(emu->port + A_IOCFG); + size = ARRAY_SIZE(i2c_adc_init); + for (n = 0; n < size; n++) + snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]); + for (n=0; n < 4; n++) { + emu->i2c_capture_volume[n][0]= 0xcf; + emu->i2c_capture_volume[n][1]= 0xcf; + } + + } + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ @@ -274,6 +305,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) if (enable_ir) { /* enable IR for SB Live */ if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); @@ -293,6 +326,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { /* enable analog output */ unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); @@ -311,6 +346,8 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) /* Enable analog/digital outs on audigy */ if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); @@ -1139,10 +1176,11 @@ static struct snd_emu_chip_details emu_chip_details[] = { .adc_1361t = 1, /* 24 bit capture instead of 16bit */ .ac97_chip = 1} , /* Audigy 2 ZS Notebook Cardbus card.*/ - /* Tested by James@superbug.co.uk 22th December 2005 */ + /* Tested by James@superbug.co.uk 6th November 2006 */ /* Audio output 7.1/Headphones working. * Digital output working. (AC3 not checked, only PCM) - * Audio inputs not tested. + * Audio Mic/Line inputs working. + * Digital input not tested. */ /* DSP: Tina2 * DAC: Wolfson WM8768/WM8568 @@ -1150,6 +1188,25 @@ static struct snd_emu_chip_details emu_chip_details[] = { * AC97: None * CA0151: None */ + /* Tested by James@superbug.co.uk 4th April 2006 */ + /* A_IOCFG bits + * Output + * 0: Not Used + * 1: 0 = Mute all the 7.1 channel out. 1 = unmute. + * 2: Analog input 0 = line in, 1 = mic in + * 3: Not Used + * 4: Digital output 0 = off, 1 = on. + * 5: Not Used + * 6: Not Used + * 7: Not Used + * Input + * All bits 1 (0x3fxx) means nothing plugged in. + * 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing. + * A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing. + * C-D: 2 = Front/Rear/etc, 3 = nothing. + * E-F: Always 0 + * + */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102, .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", .id = "Audigy2", @@ -1157,6 +1214,7 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .ca_cardbus_chip = 1, .spi_dac = 1, + .i2c_adc = 1, .spk71 = 1} , {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 9b7fd564343e..b8221f385ff9 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -36,9 +36,14 @@ #include #include #include +#include + +#include "p17v.h" #define AC97_ID_STAC9758 0x83847658 +static DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */ + static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; @@ -579,6 +584,162 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock = .put = snd_emu1010_internal_clock_put }; +static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ +#if 0 + static char *texts[4] = { + "Unknown1", "Unknown2", "Mic", "Line" + }; +#endif + static char *texts[2] = { + "Mic", "Line" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; + return 0; +} + +static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int source_id; + unsigned int ngain, ogain; + u32 gpio; + int change = 0; + unsigned long flags; + u32 source; + /* If the capture source has changed, + * update the capture volume from the cached value + * for the particular source. + */ + source_id = ucontrol->value.enumerated.item[0]; /* Use 2 and 3 */ + change = (emu->i2c_capture_source != source_id); + if (change) { + snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */ + spin_lock_irqsave(&emu->emu_lock, flags); + gpio = inl(emu->port + A_IOCFG); + if (source_id==0) + outl(gpio | 0x4, emu->port + A_IOCFG); + else + outl(gpio & ~0x4, emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->emu_lock, flags); + + ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ + if (ngain != ogain) + snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); + ngain = emu->i2c_capture_volume[source_id][1]; /* Right */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ + if (ngain != ogain) + snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + + source = 1 << (source_id + 2); + snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */ + emu->i2c_capture_source = source_id; + } + return change; +} + +static struct snd_kcontrol_new snd_audigy_i2c_capture_source = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_audigy_i2c_capture_source_info, + .get = snd_audigy_i2c_capture_source_get, + .put = snd_audigy_i2c_capture_source_put +}; + +static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + int source_id; + + source_id = kcontrol->private_value; + + ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; + ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; + return 0; +} + +static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int ogain; + unsigned int ngain; + int source_id; + int change = 0; + + source_id = kcontrol->private_value; + ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ngain = ucontrol->value.integer.value[0]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); + emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; + change = 1; + } + ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ + ngain = ucontrol->value.integer.value[1]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; + change = 1; + } + + return change; +} + +#define I2C_VOLUME(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = snd_audigy_i2c_volume_info, \ + .get = snd_audigy_i2c_volume_get, \ + .put = snd_audigy_i2c_volume_put, \ + .tlv = { .p = snd_audigy_db_scale2 }, \ + .private_value = chid \ +} + + +static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = { + I2C_VOLUME("Mic Capture Volume", 0), + I2C_VOLUME("Line Capture Volume", 0) +}; + #if 0 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1179,7 +1340,9 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, int change = 0; spin_lock_irqsave(&emu->reg_lock, flags); - if (emu->audigy) { + if ( emu->card_capabilities->i2c_adc) { + /* Do nothing for Audigy 2 ZS Notebook */ + } else if (emu->audigy) { reg = inl(emu->port + A_IOCFG); val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0; change = (reg & A_IOCFG_GPOUT0) != val; @@ -1317,6 +1480,22 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "AMic Playback Volume", "Mic Playback Volume", NULL }; + static char *audigy_rename_ctls_i2c_adc[] = { + //"Analog Mix Capture Volume","OLD Analog Mix Capture Volume", + "Line Capture Volume", "Analog Mix Capture Volume", + "Wave Playback Volume", "OLD PCM Playback Volume", + "Wave Master Playback Volume", "Master Playback Volume", + "AMic Playback Volume", "Old Mic Playback Volume", + NULL + }; + static char *audigy_remove_ctls_i2c_adc[] = { + /* On the Audigy2 ZS Notebook + * Capture via WM8775 */ + "Mic Capture Volume", + "Analog Mix Capture Volume", + "Aux Capture Volume", + NULL + }; static char *audigy_remove_ctls_1361t_adc[] = { /* On the Audigy2 the AC97 playback is piped into * the Philips ADC for 24bit capture */ @@ -1409,6 +1588,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, } for (; *c; c++) remove_ctl(card, *c); + } else if (emu->card_capabilities->i2c_adc) { + c = audigy_remove_ctls_i2c_adc; + for (; *c; c++) + remove_ctl(card, *c); } else { no_ac97: if (emu->card_capabilities->ecard) @@ -1422,6 +1605,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if (emu->audigy) if (emu->card_capabilities->adc_1361t) c = audigy_rename_ctls_1361t_adc; + else if (emu->card_capabilities->i2c_adc) + c = audigy_rename_ctls_i2c_adc; else c = audigy_rename_ctls; else @@ -1584,6 +1769,20 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if (err < 0) return err; } + + if ( emu->card_capabilities->i2c_adc) { + int i; + + err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu)); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu)); + if (err < 0) + return err; + } + } return 0; } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 27ab7d1788a0..116e1c8d9361 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -30,6 +30,7 @@ #include #include #include +#include "p17v.h" unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) { @@ -167,6 +168,64 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, return 0; } +/* The ADC does not support i2c read, so only write is implemented */ +int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, + u32 reg, + u32 value) +{ + u32 tmp; + int timeout = 0; + int status; + int retry; + if ((reg > 0x7f) || (value > 0x1ff)) { + snd_printk(KERN_ERR "i2c_write: invalid values.\n"); + return -EINVAL; + } + + tmp = reg << 25 | value << 16; + // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value); + /* Not sure what this I2C channel controls. */ + /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */ + + /* This controls the I2C connected to the WM8775 ADC Codec */ + snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp); + tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */ + + for (retry = 0; retry < 10; retry++) { + /* Send the data to i2c */ + //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0); + //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + tmp = 0; + tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); + snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp); + + /* Wait till the transaction ends */ + while (1) { + udelay(10); + status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0); + // snd_printk("I2C:status=0x%x\n", status); + timeout++; + if ((status & I2C_A_ADC_START) == 0) + break; + + if (timeout > 1000) { + snd_printk("emu10k1:I2C:timeout status=0x%x\n", status); + break; + } + } + //Read back and see if the transaction is successful + if ((status & I2C_A_ADC_ABORT) == 0) + break; + } + + if (retry == 10) { + snd_printk(KERN_ERR "Writing to ADC failed!\n"); + return -EINVAL; + } + + return 0; +} + int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value) { if (reg < 0 || reg > 0x3f) diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h index 7ddb5be632cf..4ef5f68a9cd0 100644 --- a/sound/pci/emu10k1/p17v.h +++ b/sound/pci/emu10k1/p17v.h @@ -43,6 +43,53 @@ #define P17V_I2C_ADDR 0x3d /* I2C Address */ #define P17V_I2C_0 0x3e /* I2C Data */ #define P17V_I2C_1 0x3f /* I2C Data */ +/* I2C values */ +#define I2C_A_ADC_ADD_MASK 0x000000fe /*The address is a 7 bit address */ +#define I2C_A_ADC_RW_MASK 0x00000001 /*bit mask for R/W */ +#define I2C_A_ADC_TRANS_MASK 0x00000010 /*Bit mask for I2c address DAC value */ +#define I2C_A_ADC_ABORT_MASK 0x00000020 /*Bit mask for I2C transaction abort flag */ +#define I2C_A_ADC_LAST_MASK 0x00000040 /*Bit mask for Last word transaction */ +#define I2C_A_ADC_BYTE_MASK 0x00000080 /*Bit mask for Byte Mode */ + +#define I2C_A_ADC_ADD 0x00000034 /*This is the Device address for ADC */ +#define I2C_A_ADC_READ 0x00000001 /*To perform a read operation */ +#define I2C_A_ADC_START 0x00000100 /*Start I2C transaction */ +#define I2C_A_ADC_ABORT 0x00000200 /*I2C transaction abort */ +#define I2C_A_ADC_LAST 0x00000400 /*I2C last transaction */ +#define I2C_A_ADC_BYTE 0x00000800 /*I2C one byte mode */ + +#define I2C_D_ADC_REG_MASK 0xfe000000 /*ADC address register */ +#define I2C_D_ADC_DAT_MASK 0x01ff0000 /*ADC data register */ + +#define ADC_TIMEOUT 0x00000007 /*ADC Timeout Clock Disable */ +#define ADC_IFC_CTRL 0x0000000b /*ADC Interface Control */ +#define ADC_MASTER 0x0000000c /*ADC Master Mode Control */ +#define ADC_POWER 0x0000000d /*ADC PowerDown Control */ +#define ADC_ATTEN_ADCL 0x0000000e /*ADC Attenuation ADCL */ +#define ADC_ATTEN_ADCR 0x0000000f /*ADC Attenuation ADCR */ +#define ADC_ALC_CTRL1 0x00000010 /*ADC ALC Control 1 */ +#define ADC_ALC_CTRL2 0x00000011 /*ADC ALC Control 2 */ +#define ADC_ALC_CTRL3 0x00000012 /*ADC ALC Control 3 */ +#define ADC_NOISE_CTRL 0x00000013 /*ADC Noise Gate Control */ +#define ADC_LIMIT_CTRL 0x00000014 /*ADC Limiter Control */ +#define ADC_MUX 0x00000015 /*ADC Mux offset */ +#if 0 +/* FIXME: Not tested yet. */ +#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain +#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB +#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute +#define ADC_MUTE 0x000000c0 //Value to mute ADC +#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select +#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock +#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter +#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window +#endif + +#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux +#define ADC_MUX_0 0x00000001 //Value to select Unknown at ADC Mux (Not used) +#define ADC_MUX_1 0x00000002 //Value to select Unknown at ADC Mux (Not used) +#define ADC_MUX_2 0x00000004 //Value to select Mic at ADC Mux +#define ADC_MUX_3 0x00000008 //Value to select Line-In at ADC Mux #define P17V_START_AUDIO 0x40 /* Start Audio bit */ /* 41 - 47: Reserved */ -- cgit v1.2.3-59-g8ed1b From eb41dab6e10332c1c9008f3cfc5b88ff1e392cb9 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Wed, 6 Dec 2006 20:38:45 +0000 Subject: [ALSA] emu10k1: Rename the digital optical capture control for the Audigy 2 ZS Notebook. Digital playback and capture now works, but it is not bit accurate because it passes through a resampler. Bit accurate playback and capture will be implemented later via the p17v. Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- sound/pci/emu10k1/emumixer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index b8221f385ff9..0469546fc333 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1486,6 +1486,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Wave Playback Volume", "OLD PCM Playback Volume", "Wave Master Playback Volume", "Master Playback Volume", "AMic Playback Volume", "Old Mic Playback Volume", + "CD Capture Volume", "IEC958 Optical Capture Volume", NULL }; static char *audigy_remove_ctls_i2c_adc[] = { @@ -1494,6 +1495,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Mic Capture Volume", "Analog Mix Capture Volume", "Aux Capture Volume", + "IEC958 Optical Capture Volume", NULL }; static char *audigy_remove_ctls_1361t_adc[] = { -- cgit v1.2.3-59-g8ed1b From e0e6ce0380e0c4de35371372bc5b6c2b02458597 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 7 Dec 2006 08:22:50 +0100 Subject: [ALSA] add struct snd_pcm_substream forward declaration fixes: include/sound/pcm.h:62: warning: 'struct snd_pcm_substream' declared inside parameter list Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- include/sound/pcm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index ec006ed8cd59..ee6bc2d06803 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -56,6 +56,8 @@ struct snd_pcm_hardware { size_t fifo_size; /* fifo size in bytes */ }; +struct snd_pcm_substream; + struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); -- cgit v1.2.3-59-g8ed1b From 61e77107fa849b69f50ebe96217ba3468a216ba8 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Thu, 7 Dec 2006 08:24:12 +0100 Subject: [ALSA] create device symlink in snd-aoa create sysfs device symlinks for snd-aoa in /sys/class/sound/controlC0 This allows hald to recognize the device as sound device. Furthermore it allows the desktop user to actually access the sound device nodes. hald and related packages will modify the acl attributes. Fixes https://bugzilla.novell.com/show_bug.cgi?id=106294 Acked-by: Johannes Berg Signed-off-by: Olaf Hering Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- sound/aoa/aoa.h | 2 +- sound/aoa/core/snd-aoa-alsa.c | 3 ++- sound/aoa/core/snd-aoa-alsa.h | 2 +- sound/aoa/core/snd-aoa-core.c | 4 ++-- sound/aoa/fabrics/snd-aoa-fabric-layout.c | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h index 378ef1e9879b..541b908f3cdf 100644 --- a/sound/aoa/aoa.h +++ b/sound/aoa/aoa.h @@ -99,7 +99,7 @@ struct aoa_fabric { * that are not assigned yet are passed to the fabric * again for reconsideration. */ extern int -aoa_fabric_register(struct aoa_fabric *fabric); +aoa_fabric_register(struct aoa_fabric *fabric, struct device *dev); /* it is vital to call this when the fabric exits! * When calling, the remove_codec will be called diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c index 8c5a19bd602a..17fe689ed287 100644 --- a/sound/aoa/core/snd-aoa-alsa.c +++ b/sound/aoa/core/snd-aoa-alsa.c @@ -14,7 +14,7 @@ MODULE_PARM_DESC(index, "index for AOA sound card."); static struct aoa_card *aoa_card; -int aoa_alsa_init(char *name, struct module *mod) +int aoa_alsa_init(char *name, struct module *mod, struct device *dev) { struct snd_card *alsa_card; int err; @@ -28,6 +28,7 @@ int aoa_alsa_init(char *name, struct module *mod) return -ENOMEM; aoa_card = alsa_card->private_data; aoa_card->alsa_card = alsa_card; + alsa_card->dev = dev; strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)); strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname)); strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname)); diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/snd-aoa-alsa.h index 660d2f1793bb..9669e4489cab 100644 --- a/sound/aoa/core/snd-aoa-alsa.h +++ b/sound/aoa/core/snd-aoa-alsa.h @@ -10,7 +10,7 @@ #define __SND_AOA_ALSA_H #include "../aoa.h" -extern int aoa_alsa_init(char *name, struct module *mod); +extern int aoa_alsa_init(char *name, struct module *mod, struct device *dev); extern void aoa_alsa_cleanup(void); #endif /* __SND_AOA_ALSA_H */ diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c index ecd2d8263f2d..19fdae400687 100644 --- a/sound/aoa/core/snd-aoa-core.c +++ b/sound/aoa/core/snd-aoa-core.c @@ -82,7 +82,7 @@ void aoa_codec_unregister(struct aoa_codec *codec) } EXPORT_SYMBOL_GPL(aoa_codec_unregister); -int aoa_fabric_register(struct aoa_fabric *new_fabric) +int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev) { struct aoa_codec *c; int err; @@ -98,7 +98,7 @@ int aoa_fabric_register(struct aoa_fabric *new_fabric) if (!new_fabric) return -EINVAL; - err = aoa_alsa_init(new_fabric->name, new_fabric->owner); + err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev); if (err) return err; diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index 172eb95476c0..4b8e32d1ebf9 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -1014,7 +1014,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) ldev->gpio.methods->init(&ldev->gpio); - err = aoa_fabric_register(&layout_fabric); + err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev); if (err && err != -EALREADY) { printk(KERN_INFO "snd-aoa-fabric-layout: can't use," " another fabric is active!\n"); -- cgit v1.2.3-59-g8ed1b From c17d6fd90a336d2b971dc9f51338f9540479b263 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Thu, 7 Dec 2006 08:25:01 +0100 Subject: [ALSA] create driver symlink in snd-aoa /sys/bus/aoa-soundbus/devices/*/ create sysfs driver symlink for snd-aoa in /sys/bus/aoa-soundbus/devices/*/ Acked-by: Johannes Berg Signed-off-by: Olaf Hering Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- sound/aoa/fabrics/snd-aoa-fabric-layout.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index 4b8e32d1ebf9..409809600ddc 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -1107,6 +1107,9 @@ static struct soundbus_driver aoa_soundbus_driver = { .suspend = aoa_fabric_layout_suspend, .resume = aoa_fabric_layout_resume, #endif + .driver = { + .owner = THIS_MODULE, + } }; static int __init aoa_fabric_layout_init(void) -- cgit v1.2.3-59-g8ed1b From a5f65029ad5c5262ee3aff5165698e431415cf7c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 7 Dec 2006 08:26:27 +0100 Subject: [ALSA] arm header fix Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- sound/arm/aaci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h index 06295190606c..9175ff9ded01 100644 --- a/sound/arm/aaci.h +++ b/sound/arm/aaci.h @@ -228,7 +228,7 @@ struct aaci { /* AC'97 */ struct mutex ac97_sem; - ac97_bus_t *ac97_bus; + struct snd_ac97_bus *ac97_bus; u32 maincr; spinlock_t lock; -- cgit v1.2.3-59-g8ed1b From 7c157069bc953c3cfb5926e92d358e46423bf942 Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Sun, 10 Dec 2006 00:00:38 +0000 Subject: [ALSA] ca0106: Fix sound capture on Audigy LS via AC97. Fixes ALSA bug#2286 Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- sound/pci/ca0106/ca0106_main.c | 19 ++++++++++++++++--- sound/pci/ca0106/ca0106_mixer.c | 42 +++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index f61f052f6d14..6f781b811876 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1382,7 +1382,6 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card, snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf); snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */ chip->spdif_enable = 0; /* Set digital SPDIF output off */ - chip->capture_source = 3; /* Set CAPTURE_SOURCE */ //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */ //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */ @@ -1402,8 +1401,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card, snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */ } - snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */ - chip->capture_source = 3; /* Set CAPTURE_SOURCE */ + if (chip->details->i2c_adc == 1) { + /* Select MIC, Line in, TAD in, AUX in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); + /* Default to CAPTURE_SOURCE to i2s in */ + chip->capture_source = 3; + } else if (chip->details->ac97 == 1) { + /* Default to AC97 in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4); + /* Default to CAPTURE_SOURCE to AC97 in */ + chip->capture_source = 4; + } else { + /* Select MIC, Line in, TAD in, AUX in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); + /* Default to Set CAPTURE_SOURCE to i2s in */ + chip->capture_source = 3; + } if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index bd2a054c673b..289f78a41608 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -482,19 +482,6 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, .private_value = ((chid) << 8) | (reg) \ } -#define I2C_VOLUME(xname,chid) \ -{ \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ - .info = snd_ca0106_i2c_volume_info, \ - .get = snd_ca0106_i2c_volume_get, \ - .put = snd_ca0106_i2c_volume_put, \ - .tlv = { .p = snd_ca0106_db_scale2 }, \ - .private_value = chid \ -} - - static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("Analog Front Playback Volume", CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), @@ -517,11 +504,6 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("CAPTURE feedback Playback Volume", 1, CAPTURE_CONTROL), - I2C_VOLUME("Phone Capture Volume", 0), - I2C_VOLUME("Mic Capture Volume", 1), - I2C_VOLUME("Line in Capture Volume", 2), - I2C_VOLUME("Aux Capture Volume", 3), - { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -561,6 +543,25 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { }, }; +#define I2C_VOLUME(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = snd_ca0106_i2c_volume_info, \ + .get = snd_ca0106_i2c_volume_get, \ + .put = snd_ca0106_i2c_volume_put, \ + .tlv = { .p = snd_ca0106_db_scale2 }, \ + .private_value = chid \ +} + +static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = { + I2C_VOLUME("Phone Capture Volume", 0), + I2C_VOLUME("Mic Capture Volume", 1), + I2C_VOLUME("Line in Capture Volume", 2), + I2C_VOLUME("Aux Capture Volume", 3), +}; + static int __devinit remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; @@ -645,6 +646,11 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) return err; } if (emu->details->i2c_adc == 1) { + for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu)); + if (err < 0) + return err; + } if (emu->details->gpio_type == 1) err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); else /* gpio_type == 2 */ -- cgit v1.2.3-59-g8ed1b From cbb7d8f9b7b0a9f51c9869d0da63ea75a2c95caf Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Wed, 13 Dec 2006 11:21:55 +0000 Subject: [ALSA] emu10k1: Update registers defines for the Audigy 2/emu10k2.5 Signed-off-by: James Courtier-Dutton Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 63 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 32ce4bd31b46..adca71b20daa 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -460,6 +460,7 @@ #define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ #define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ +#define A_HR 0x0b /* High Resolution. 24bit playback from host to DSP. */ #define MAPA 0x0c /* Cache map A */ #define MAPB 0x0d /* Cache map B */ @@ -467,6 +468,8 @@ #define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ #define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ +/* 0x0e, 0x0f: Not used */ + #define ENVVOL 0x10 /* Volume envelope register */ #define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ /* 0x8000-n == 666*n usec delay */ @@ -555,7 +558,7 @@ /* NOTE: All channels contain internal variables; do */ /* not write to these locations. */ -/* 1f something */ +/* 0x1f: not used */ #define CD0 0x20 /* Cache data 0 register */ #define CD1 0x21 /* Cache data 1 register */ @@ -625,6 +628,8 @@ #define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ #define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ +#define A_TBLSZ ` 0x43 /* Effects Tank Internal Table Size. Only low byte or register used */ + #define TCBS 0x44 /* Tank cache buffer size register */ #define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ #define TCBS_BUFFSIZE_16K 0x00000000 @@ -645,7 +650,7 @@ #define FXBA 0x47 /* FX Buffer Address */ #define FXBA_MASK 0xfffff000 /* 20 bit base address */ -/* 0x48 something - word access, defaults to 3f */ +#define A_HWM 0x48 /* High PCI Water Mark - word access, defaults to 3f */ #define MICBS 0x49 /* Microphone buffer size register */ @@ -689,6 +694,18 @@ #define ADCBS_BUFSIZE_57344 0x0000001e #define ADCBS_BUFSIZE_65536 0x0000001f +/* Current Send B, A Amounts */ +#define A_CSBA 0x4c + +/* Current Send D, C Amounts */ +#define A_CSDC 0x4d + +/* Current Send F, E Amounts */ +#define A_CSFE 0x4e + +/* Current Send H, G Amounts */ +#define A_CSHG 0x4f + #define CDCS 0x50 /* CD-ROM digital channel status register */ @@ -696,6 +713,9 @@ #define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ +/* S/PDIF Input C Channel Status */ +#define A_SPSC 0x52 + #define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ #define A_DBG 0x53 @@ -736,6 +756,8 @@ #define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ #define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ +/* 0x57: Not used */ + /* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ #define CLIEL 0x58 /* Channel loop interrupt enable low register */ @@ -761,6 +783,9 @@ #define AC97SLOT_CNTR 0x10 /* Center enable */ #define AC97SLOT_LFE 0x20 /* LFE enable */ +/* PCB Revision */ +#define A_PCB 0x5f + // NOTE: 0x60,61,62: 64-bit #define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ @@ -808,9 +833,18 @@ #define HLIPH 0x69 /* Channel half loop interrupt pending high register */ -// 0x6a,6b,6c used for some recording -// 0x6d unused -// 0x6e,6f - tanktable base / offset +/* S/PDIF Host Record Index (bypasses SRC) */ +#define A_SPRI 0x6a +/* S/PDIF Host Record Address */ +#define A_SPRA 0x6b +/* S/PDIF Host Record Control */ +#define A_SPRC 0x6c +/* Delayed Interrupt Counter & Enable */ +#define A_DICE 0x6d +/* Tank Table Base */ +#define A_TTB 0x6e +/* Tank Delay Offset */ +#define A_TDOF 0x6f /* This is the MPU port on the card (via the game port) */ #define A_MUDATA1 0x70 @@ -828,6 +862,7 @@ #define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ #define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ +/* Extended Hardware Control */ #define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ #define A_SAMPLE_RATE 0x76 /* Various sample rate settings. */ #define A_SAMPLE_RATE_NOT_USED 0x0ffc111e /* Bits that are not used and cannot be set. */ @@ -850,8 +885,20 @@ #define A_PCM_96000 0x00004000 #define A_PCM_44100 0x00008000 -/* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell */ -/* 0x7a, 0x7b - lookup tables */ +/* I2S0 Sample Rate Tracker Status */ +#define A_SRT3 0x77 + +/* I2S1 Sample Rate Tracker Status */ +#define A_SRT4 0x78 + +/* I2S2 Sample Rate Tracker Status */ +#define A_SRT5 0x79 +/* - default to 0x01080000 on my audigy 2 ZS --rlrevell */ + +/* Tank Table DMA Address */ +#define A_TTDA 0x7a +/* Tank Table DMA Data */ +#define A_TTDD 0x7b #define A_FXRT2 0x7c #define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */ @@ -873,7 +920,7 @@ #define A_FXRT_CHANNELC 0x003f0000 #define A_FXRT_CHANNELD 0x3f000000 - +/* 0x7f: Not used */ /* Each FX general purpose register is 32 bits in length, all bits are used */ #define FXGPREGBASE 0x100 /* FX general purpose registers base */ #define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ -- cgit v1.2.3-59-g8ed1b From ca377fecdd822f9ef5b0a21586040e7d0e1d0c7a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 15 Dec 2006 09:26:20 +0100 Subject: [ALSA] ucb1400_ts.c compilation fix (struct snd_ac97) From: Andrew Morton Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- drivers/input/touchscreen/ucb1400_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 4358a0a78eaa..c7db4032ef02 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -83,7 +83,7 @@ struct ucb1400 { - ac97_t *ac97; + struct snd_ac97 *ac97; struct input_dev *ts_idev; int irq; -- cgit v1.2.3-59-g8ed1b From 4484bb2e93a9ab636d149edc6515c75ea224e2b0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 15 Dec 2006 09:30:07 +0100 Subject: [ALSA] Fix the soc code after dhowells workqueue changes. From: Andrew Morton I converted the workqueues to per-device while I was there. It seems strange to create a new kernel thread (on each CPU!) and to then only have a single global work to ever be queued upon it. Plus without this, I'd have to use the _NAR stuff, gawd help me. Does that workqueue really need to be per-cpu? Does that workqueue really need to exist? Why not use keventd? Cc: Takashi Iwai Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 3dfe052e0788..c985a111bc3f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -454,6 +455,7 @@ struct snd_soc_device { struct snd_soc_platform *platform; struct snd_soc_codec *codec; struct snd_soc_codec_device *codec_dev; + struct delayed_work delayed_work; void *codec_data; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 90e8841e7e33..0bae14145a03 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -56,7 +56,6 @@ static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(io_mutex); static struct workqueue_struct *soc_workq; -static struct work_struct soc_stream_work; static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); /* supported sample rates */ @@ -728,9 +727,10 @@ out: * This is to ensure there are no pops or clicks in between any music tracks * due to DAPM power cycling. */ -static void close_delayed_work(void *data) +static void close_delayed_work(struct work_struct *work) { - struct snd_soc_device *socdev = data; + struct snd_soc_device *socdev = + container_of(work, struct snd_soc_device, delayed_work.work); struct snd_soc_codec *codec = socdev->codec; struct snd_soc_codec_dai *codec_dai; int i; @@ -805,7 +805,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* start delayed pop wq here for playback streams */ rtd->codec_dai->pop_wait = 1; - queue_delayed_work(soc_workq, &soc_stream_work, + queue_delayed_work(soc_workq, &socdev->delayed_work, msecs_to_jiffies(pmdown_time)); } else { /* capture streams can be powered down now */ @@ -865,7 +865,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) SND_SOC_DAPM_STREAM_START); else { rtd->codec_dai->pop_wait = 0; - cancel_delayed_work(&soc_stream_work); + cancel_delayed_work(&socdev->delayed_work); if (rtd->codec_dai->digital_mute) rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); } @@ -1225,7 +1225,7 @@ static int soc_probe(struct platform_device *pdev) soc_workq = create_workqueue("kdapm"); if (soc_workq == NULL) goto work_err; - INIT_WORK(&soc_stream_work, close_delayed_work, socdev); + INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); return 0; work_err: -- cgit v1.2.3-59-g8ed1b From 9102cd1c35c9be223e0f60b7c42cb581f0d42f1a Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Fri, 15 Dec 2006 10:02:12 +0100 Subject: [ALSA] hda-codec (realtek): add support for MacPro series workstations This patch adds limited support for Intel-based MacPro workstations. Currently, the front headphone jack is not functioning, but line out and line in are working. S/PDIF not tested. Signed-off-by: Tobin Davis Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 112 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a1b6c9661d45..c4a06c5f7d93 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -112,6 +112,7 @@ enum { ALC882_6ST_DIG, ALC882_ARIMA, ALC882_AUTO, + ALC885_MACPRO, ALC882_MODEL_LAST, }; @@ -4507,6 +4508,100 @@ static struct hda_verb alc882_eapd_verbs[] = { { } }; +/* Mac Pro test */ +static struct snd_kcontrol_new alc882_macpro_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc882_macpro_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Speaker: output */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* Headphone output (output 0 - 0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; +static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) +{ + unsigned int gpiostate, gpiomask, gpiodir; + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (!muted) + gpiostate |= (1 << pin); + else + gpiostate &= ~(1 << pin); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= (1 << pin); + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= (1 << pin); + + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); + + msleep(1); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -4633,6 +4728,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC882_3ST_DIG] = "3stack-dig", [ALC882_6ST_DIG] = "6stack-dig", [ALC882_ARIMA] = "arima", + [ALC885_MACPRO] = "macpro", [ALC882_AUTO] = "auto", }; @@ -4677,6 +4773,17 @@ static struct alc_config_preset alc882_presets[] = { .channel_mode = alc882_sixstack_modes, .input_mux = &alc882_capture_source, }, + [ALC885_MACPRO] = { + .mixers = { alc882_macpro_mixer }, + .init_verbs = { alc882_macpro_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + }, }; @@ -4804,6 +4911,11 @@ static int patch_alc882(struct hda_codec *codec) if (board_config != ALC882_AUTO) setup_preset(spec, &alc882_presets[board_config]); + if (board_config == ALC885_MACPRO) { + alc882_gpio_mute(codec, 0, 0); + alc882_gpio_mute(codec, 1, 0); + } + spec->stream_name_analog = "ALC882 Analog"; spec->stream_analog_playback = &alc882_pcm_analog_playback; spec->stream_analog_capture = &alc882_pcm_analog_capture; -- cgit v1.2.3-59-g8ed1b From 1a5965b72209db9db453bc0049393e0d54cf85cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Dec 2006 13:07:35 +0100 Subject: [ALSA] Fix AC97_BUS in soc/pxa/Kconfig Fixed the renamed AC97_BUS in soc/pxa/Kconfig file. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a07598cdab3c..579e1c8d2b28 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -15,7 +15,7 @@ config SND_PXA2XX_AC97 config SND_PXA2XX_SOC_AC97 tristate - select SND_AC97_BUS + select AC97_BUS select SND_SOC_AC97_BUS config SND_PXA2XX_SOC_I2S -- cgit v1.2.3-59-g8ed1b From cdf88efa03907a884177b226321bb41bc17c407f Mon Sep 17 00:00:00 2001 From: Toshimune Konno Date: Mon, 18 Dec 2006 13:12:18 +0100 Subject: [ALSA] ice1724 - Add support for Prodigy 7.1 XT This patch supports Audiotrack 7.1 XT. 7.1XT is almost same hardware as 7.1LT. so using 7.1 LT's code. Signed-off-by: Toshimune Konno Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ice1712/aureon.c | 52 ++++++++++++++++++++++++++++++++++++++-------- sound/pci/ice1712/aureon.h | 4 +++- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 9e76cebd2d22..a085618d33f9 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -474,7 +474,8 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned tmp = snd_ice1712_gpio_read(ice); - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT) { + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT || + ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) { snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS)); mosi = PRODIGY_SPI_MOSI; clk = PRODIGY_SPI_CLK; @@ -601,7 +602,9 @@ static unsigned short wm_get(struct snd_ice1712 *ice, int reg) static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val) { aureon_spi_write(ice, - (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ? PRODIGY_WM_CS : AUREON_WM_CS), + ((ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT || + ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) ? + PRODIGY_WM_CS : AUREON_WM_CS), (reg << 9) | (val & 0x1ff), 16); } @@ -1288,12 +1291,14 @@ static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable) tmp2 = tmp = snd_ice1712_gpio_read(ice); if (enable) - if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && + ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) tmp |= AUREON_HP_SEL; else tmp |= PRODIGY_HP_SEL; else - if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && + ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) tmp &= ~ AUREON_HP_SEL; else tmp &= ~ PRODIGY_HP_SEL; @@ -1898,7 +1903,8 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice) return err; } } - else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) { + else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && + ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) { for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) { err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice)); if (err < 0) @@ -1906,7 +1912,8 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice) } } - if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) { + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && + ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) { unsigned char id; snd_ice1712_save_gpio_status(ice); id = aureon_cs8415_get(ice, CS8415_ID); @@ -2062,7 +2069,8 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) /* initialize WM8770 codec */ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 || - ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT) + ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT || + ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) p = wm_inits_prodigy; else p = wm_inits_aureon; @@ -2070,7 +2078,8 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) wm_put(ice, p[0], p[1]); /* initialize CS8415A codec */ - if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) { + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && + ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) { for (p = cs_inits; *p != (unsigned short)-1; p++) aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24); ice->spec.aureon.cs8415_mux = 1; @@ -2163,7 +2172,22 @@ static unsigned char prodigy71lt_eeprom[] __devinitdata = { 0x00, /* GPIO_STATE1 */ 0x00, /* GPIO_STATE2 */ }; - + +static unsigned char prodigy71xt_eeprom[] __devinitdata = { + 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; /* entry point */ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = { @@ -2217,5 +2241,15 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = { .eeprom_data = prodigy71lt_eeprom, .driver = "Prodigy71LT", }, + { + .subvendor = VT1724_SUBDEVICE_PRODIGY71XT, + .name = "Audiotrak Prodigy 7.1 XT", + .model = "prodigy71xt", + .chip_init = aureon_init, + .build_controls = aureon_add_controls, + .eeprom_size = sizeof(prodigy71xt_eeprom), + .eeprom_data = prodigy71xt_eeprom, + .driver = "Prodigy71LT", + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h index 3b7bea656c57..c253b8e2c789 100644 --- a/sound/pci/ice1712/aureon.h +++ b/sound/pci/ice1712/aureon.h @@ -28,13 +28,15 @@ "{Terratec,Aureon 7.1 Space},"\ "{Terratec,Aureon 7.1 Universe}," \ "{AudioTrak,Prodigy 7.1}," \ - "{AudioTrak,Prodigy 7.1 LT}," + "{AudioTrak,Prodigy 7.1 LT},"\ + "{AudioTrak,Prodigy 7.1 XT}," #define VT1724_SUBDEVICE_AUREON51_SKY 0x3b154711 /* Aureon 5.1 Sky */ #define VT1724_SUBDEVICE_AUREON71_SPACE 0x3b154511 /* Aureon 7.1 Space */ #define VT1724_SUBDEVICE_AUREON71_UNIVERSE 0x3b155311 /* Aureon 7.1 Universe */ #define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */ #define VT1724_SUBDEVICE_PRODIGY71LT 0x32315441 /* PRODIGY 7.1 LT */ +#define VT1724_SUBDEVICE_PRODIGY71XT 0x36315441 /* PRODIGY 7.1 XT*/ extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[]; -- cgit v1.2.3-59-g8ed1b From 333824034a19baf71b2bd5fe2153630982f379b0 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 18 Dec 2006 13:17:28 +0100 Subject: [ALSA] hda: add sigmatel 9205 eapd support Adds support for handling EAPD on 9205 codecs Signed-off-by: Matt Porter Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_sigmatel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index cbaa00aa5b92..4e3fc95b7b4f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1859,6 +1859,18 @@ static int patch_stac9205(struct hda_codec *codec) spec->multiout.dac_nids = spec->dac_nids; + /* Configure GPIO0 as EAPD output */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x00000001); + /* Configure GPIO0 as CMOS */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000); + /* Assert GPIO0 high */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, 0x00000001); + /* Enable GPIO0 */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, 0x00000001); + err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (err < 0) { stac92xx_free(codec); -- cgit v1.2.3-59-g8ed1b From b0148a98ec5151fec82064d95f11eb9efbc628ea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Dec 2006 13:20:06 +0100 Subject: [ALSA] snd-aoa: fix onyx resume When the machine resumes the onyx codec might be in a weird state. Hence, simply fully reset it once (and keep the code to take it out of suspend in case the suspend of the codec chip survives a reset). Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/codecs/snd-aoa-codec-onyx.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c index 0b7650788f1f..b00fc4842c93 100644 --- a/sound/aoa/codecs/snd-aoa-codec-onyx.c +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c @@ -825,7 +825,16 @@ static int onyx_resume(struct codec_info_item *cii) int err = -ENXIO; mutex_lock(&onyx->mutex); - /* take codec out of suspend */ + + /* reset codec */ + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + + /* take codec out of suspend (if it still is after reset) */ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) goto out_unlock; onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); -- cgit v1.2.3-59-g8ed1b From 4dc53e28e2e5cccb3521466be8f2ab4689ca9143 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 18 Dec 2006 13:24:37 +0100 Subject: [ALSA] hda-codec - Add quirk for Turbo-X Coeus G610P This patch adds the Turbo-X Coeus G610P to the alc880 config table, based on user provided information. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c4a06c5f7d93..18bfc3993b8d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2361,6 +2361,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), -- cgit v1.2.3-59-g8ed1b From 659eacc55a378066b60896b2bbd261ca32a10c04 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 18 Dec 2006 14:38:37 +0100 Subject: [ALSA] Remove trailing white space from wm9712.c This patch removes some trailing white space from the WM9712 ASoC codec driver. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm9712.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index c6b7de426460..36c6a38a0f94 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -45,7 +45,7 @@ static int ac97_write(struct snd_soc_codec *codec, /* may need to expand this */ static struct snd_soc_dai_mode ac97_modes[] = { { - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE, + .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE, .pcmrate = AC97_RATES, .pcmdir = AC97_DIR, }, -- cgit v1.2.3-59-g8ed1b From 0664d888a55ff99c8556690a3ae7c76dc1389008 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 18 Dec 2006 14:39:02 +0100 Subject: [ALSA] Additional credits to soc-core This patch adds copyright and credit for my good friend Richard Purdie from OpenedHand for his help and code contribution throughout the development of the core code. Many thanks Richard (I guess we overlooked this in trying to get everything working well). It also adds some extra comments wrt to DAI clock matching. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0bae14145a03..9f23901fc85c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2,8 +2,12 @@ * soc-core.c -- ALSA SoC Audio Layer * * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * * Author: Liam Girdwood * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * with code, comments and ideas from :- + * Richard Purdie * * 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 @@ -517,7 +521,8 @@ found: * Check we have matching bitclocks. If we don't then it means the * sysclock returned by either the codec or cpu DAI (selected by the * machine sysclock function) is wrong compared with the supported DAI - * modes for the codec or cpu DAI. + * modes for the codec or cpu DAI. Check your codec or CPU DAI + * config_sysclock() functions. */ if (cpu_bclk != codec_bclk && cpu_bclk){ printk(KERN_ERR -- cgit v1.2.3-59-g8ed1b From ca40587087fc05c670f4f2650cc466d557377f6d Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Mon, 18 Dec 2006 14:41:03 +0100 Subject: [ALSA] sparc dbri comment fix This is a comment fix to avoid misleading about locking in the dbri_cmdsend. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/sparc/dbri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 4ceb09d215d8..25a2a7333006 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -678,7 +678,7 @@ static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len) * The JUMP cmd points to the new cmd string. * It also releases the cmdlock spinlock. * - * Lock must not be held before calling this. + * Lock must be held before calling this. */ static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len) { -- cgit v1.2.3-59-g8ed1b From e4c3bf0f65ec9da8b067a722f734d1012ef12ceb Mon Sep 17 00:00:00 2001 From: James C Georgas Date: Tue, 19 Dec 2006 11:09:41 +0100 Subject: [ALSA] Remove AC97 POP control for STAC9708/11 The STAC9708/11 AC97 codecs implement the PCM Out Path & Mute bit in the General Purpose register (0x20:F), even though they don't implement the actual function in the mixer. Since the alsa tests for the function by toggling the bit and reading it back to see if it changed, it mistakenly creates a useless control. This patch explicitly removes the control when the codec is an STAC9708/11. I put the check in patch_sigmatel_stac9708_specific(), because I have an SBLive with this chip on it. I don't know if the STAC9758 or other codecs also behave this way. If they do, then this check could maybe go in patch_sigmatel_stac97xx_specific(), or some other more general function. Signed-off-by: James C Georgas Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 123de550d1f4..818a77d2deff 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -941,6 +941,9 @@ static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97) { int err; + /* the register bit is writable, but the function is not implemented: */ + snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL); + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback"); if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0) return err; -- cgit v1.2.3-59-g8ed1b From dc041e0b1fc918562aa3803cda166fee219a34d2 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Tue, 19 Dec 2006 14:44:15 +0100 Subject: [ALSA] sound: Change final two instances of kcalloc(1,...) to kzalloc() Change the two remaining instances in the tree of kcalloc(1,...) to the corresponding kzalloc() call. Signed-off-by: Robert P. J. Day Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 18bfc3993b8d..2be0ef9023b1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6590,7 +6590,7 @@ static int patch_alc262(struct hda_codec *codec) int board_config; int err; - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; @@ -7710,7 +7710,7 @@ static int patch_alc861(struct hda_codec *codec) int board_config; int err; - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From e250af291d6759518b574b33317eb3003012bfa2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Dec 2006 17:08:52 +0100 Subject: [ALSA] hda-codec - Use global workqueue Use global workqueue for simplicity. The unsolicited event frequency isn't so high to have own queue. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 10 ++-------- sound/pci/hda/hda_local.h | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e14faf5d5053..8f34fb447983 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -263,7 +263,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) unsol->queue[wp] = res; unsol->queue[wp + 1] = res_ex; - queue_work(unsol->workq, &unsol->work); + schedule_work(&unsol->work); return 0; } @@ -310,12 +310,6 @@ static int init_unsol_queue(struct hda_bus *bus) snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n"); return -ENOMEM; } - unsol->workq = create_singlethread_workqueue("hda_codec"); - if (! unsol->workq) { - snd_printk(KERN_ERR "hda_codec: can't create workqueue\n"); - kfree(unsol); - return -ENOMEM; - } INIT_WORK(&unsol->work, process_unsol_events); unsol->bus = bus; bus->unsol = unsol; @@ -334,7 +328,7 @@ static int snd_hda_bus_free(struct hda_bus *bus) if (! bus) return 0; if (bus->unsol) { - destroy_workqueue(bus->unsol->workq); + flush_scheduled_work(); kfree(bus->unsol); } list_for_each_safe(p, n, &bus->codec_list) { diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index b2f56d688852..39718d6cdadd 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -199,7 +199,6 @@ struct hda_bus_unsolicited { unsigned int rp, wp; /* workqueue */ - struct workqueue_struct *workq; struct work_struct work; struct hda_bus *bus; }; -- cgit v1.2.3-59-g8ed1b From 4014c38bd94156c10986a11d890bdae99437dc9a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Dec 2006 17:13:16 +0100 Subject: [ALSA] ak4114 - Use global workqueue Use global workqueue for simplicity instead of own workqueue. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ak4114.h | 1 - sound/i2c/other/ak4114.c | 17 ++++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index 2ee061625fd0..85f49d464751 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -181,7 +181,6 @@ struct ak4114 { unsigned long ccrc_errors; unsigned char rcs0; unsigned char rcs1; - struct workqueue_struct *workqueue; struct delayed_work work; void *change_callback_private; void (*change_callback)(struct ak4114 *ak4114, unsigned char c0, unsigned char c1); diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index d2f2c5078e65..69dcaf8ac793 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -66,10 +66,7 @@ static void snd_ak4114_free(struct ak4114 *chip) { chip->init = 1; /* don't schedule new work */ mb(); - if (chip->workqueue != NULL) { - flush_workqueue(chip->workqueue); - destroy_workqueue(chip->workqueue); - } + flush_scheduled_work(); kfree(chip); } @@ -106,12 +103,6 @@ int snd_ak4114_create(struct snd_card *card, for (reg = 0; reg < 5; reg++) chip->txcsb[reg] = txcsb[reg]; - chip->workqueue = create_workqueue("snd-ak4114"); - if (chip->workqueue == NULL) { - kfree(chip); - return -ENOMEM; - } - snd_ak4114_reinit(chip); chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT); @@ -143,7 +134,7 @@ void snd_ak4114_reinit(struct ak4114 *chip) chip->init = 1; mb(); - flush_workqueue(chip->workqueue); + flush_scheduled_work(); /* bring the chip to reset state and powerdown state */ reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN)); udelay(200); @@ -159,7 +150,7 @@ void snd_ak4114_reinit(struct ak4114 *chip) /* bring up statistics / event queing */ chip->init = 0; INIT_DELAYED_WORK(&chip->work, ak4114_stats); - queue_delayed_work(chip->workqueue, &chip->work, HZ / 10); + schedule_delayed_work(&chip->work, HZ / 10); } static unsigned int external_rate(unsigned char rcs1) @@ -568,7 +559,7 @@ static void ak4114_stats(struct work_struct *work) if (chip->init) return; snd_ak4114_check_rate_and_errors(chip, 0); - queue_delayed_work(chip->workqueue, &chip->work, HZ / 10); + schedule_delayed_work(&chip->work, HZ / 10); } EXPORT_SYMBOL(snd_ak4114_create); -- cgit v1.2.3-59-g8ed1b From 4bb09523de50dcf1afc5d3099b9da0381f01b04c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Dec 2006 17:16:14 +0100 Subject: [ALSA] soc - Use global workqueue Use global workqueue for simplicity instead of own workqueue in SoC core and wm8750 codes. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8750.c | 14 +++----------- sound/soc/soc-core.c | 15 ++------------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 4cc85128dc59..069b66cb18e0 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -51,7 +51,6 @@ #define warn(format, arg...) \ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) -static struct workqueue_struct *wm8750_workq = NULL; static struct work_struct wm8750_dapm_work; /* @@ -1039,7 +1038,7 @@ static int wm8750_resume(struct platform_device *pdev) if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D0; - queue_delayed_work(wm8750_workq, &wm8750_dapm_work, + schedule_delayed_work(&wm8750_dapm_work, msecs_to_jiffies(1000)); } @@ -1084,8 +1083,7 @@ static int wm8750_init(struct snd_soc_device *socdev) /* charge output caps */ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D3hot; - queue_delayed_work(wm8750_workq, &wm8750_dapm_work, - msecs_to_jiffies(1000)); + schedule_delayed_work(&wm8750_dapm_work, msecs_to_jiffies(1000)); /* set the update bits */ reg = wm8750_read_reg_cache(codec, WM8750_LDAC); @@ -1228,11 +1226,6 @@ static int wm8750_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_paths); wm8750_socdev = socdev; INIT_WORK(&wm8750_dapm_work, wm8750_work, codec); - wm8750_workq = create_workqueue("wm8750"); - if (wm8750_workq == NULL) { - kfree(codec); - return -ENOMEM; - } #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) if (setup->i2c_address) { normal_i2c[0] = setup->i2c_address; @@ -1256,8 +1249,7 @@ static int wm8750_remove(struct platform_device *pdev) if (codec->control_data) wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); - if (wm8750_workq) - destroy_workqueue(wm8750_workq); + flush_scheduled_work(); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9f23901fc85c..cf84d8251715 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -59,7 +59,6 @@ static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(io_mutex); -static struct workqueue_struct *soc_workq; static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); /* supported sample rates */ @@ -810,7 +809,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* start delayed pop wq here for playback streams */ rtd->codec_dai->pop_wait = 1; - queue_delayed_work(soc_workq, &socdev->delayed_work, + schedule_delayed_work(&socdev->delayed_work, msecs_to_jiffies(pmdown_time)); } else { /* capture streams can be powered down now */ @@ -1102,7 +1101,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) } /* close any waiting streams and save state */ - flush_workqueue(soc_workq); + flush_scheduled_work(); codec->suspend_dapm_state = codec->dapm_state; for(i = 0; i < codec->num_dai; i++) { @@ -1227,16 +1226,9 @@ static int soc_probe(struct platform_device *pdev) } /* DAPM stream work */ - soc_workq = create_workqueue("kdapm"); - if (soc_workq == NULL) - goto work_err; INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); return 0; -work_err: - if (platform->remove) - platform->remove(pdev); - platform_err: if (codec_dev->remove) codec_dev->remove(pdev); @@ -1263,9 +1255,6 @@ static int soc_remove(struct platform_device *pdev) struct snd_soc_platform *platform = socdev->platform; struct snd_soc_codec_device *codec_dev = socdev->codec_dev; - if (soc_workq) - destroy_workqueue(soc_workq); - if (platform->remove) platform->remove(pdev); -- cgit v1.2.3-59-g8ed1b From 831466f4ad2b5fe23dff77edbe6a7c244435e973 Mon Sep 17 00:00:00 2001 From: Randy Cushman Date: Tue, 19 Dec 2006 18:42:16 +0100 Subject: [ALSA] ac97 - fix microphone and line_in selection logic This patch fixes the Microphone and LINE_IN select logic for Analog Devices surround codecs with shared jacks. The existing code can never utilize the shared jacks for Microphone and LINE_IN due to the reversed jack selection logic. The patched code correctly selects the shared jack for input if the 'Channel Mode' selector does not specify that the jack is to be used for output. Specifically, in '2ch' mode the Center/LFE jack is used for microphone input and the Surround jack is used for LINE_IN, in '4ch' mode the Center/LFE jack is used for microphone input and the Surround jack is used for output, and in '6ch' mode both jacks are used for output. Signed-off-by: Randy Cushman Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 66 +++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 818a77d2deff..5f69b9c9f1b3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -190,14 +190,28 @@ static inline int is_clfe_on(struct snd_ac97 *ac97) return ac97->channel_mode >= 2; } +/* system has shared jacks with surround out enabled */ +static inline int is_shared_surrout(struct snd_ac97 *ac97) +{ + return !ac97->indep_surround && is_surround_on(ac97); +} + +/* system has shared jacks with center/lfe out enabled */ +static inline int is_shared_clfeout(struct snd_ac97 *ac97) +{ + return !ac97->indep_surround && is_clfe_on(ac97); +} + +/* system has shared jacks with line in enabled */ static inline int is_shared_linein(struct snd_ac97 *ac97) { - return ! ac97->indep_surround && is_surround_on(ac97); + return !ac97->indep_surround && !is_surround_on(ac97); } +/* system has shared jacks with mic in enabled */ static inline int is_shared_micin(struct snd_ac97 *ac97) { - return ! ac97->indep_surround && is_clfe_on(ac97); + return !ac97->indep_surround && !is_clfe_on(ac97); } @@ -2017,12 +2031,12 @@ static void alc650_update_jacks(struct snd_ac97 *ac97) { int shared; - /* shared Line-In */ - shared = is_shared_linein(ac97); + /* shared Line-In / Surround Out */ + shared = is_shared_surrout(ac97); snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9, shared ? (1 << 9) : 0); - /* update shared Mic */ - shared = is_shared_micin(ac97); + /* update shared Mic In / Center/LFE Out */ + shared = is_shared_clfeout(ac97); /* disable/enable vref */ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, shared ? (1 << 12) : 0); @@ -2152,12 +2166,12 @@ static void alc655_update_jacks(struct snd_ac97 *ac97) { int shared; - /* shared Line-In */ - shared = is_shared_linein(ac97); + /* shared Line-In / Surround Out */ + shared = is_shared_surrout(ac97); ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9, shared ? (1 << 9) : 0, 0); - /* update shared mic */ - shared = is_shared_micin(ac97); + /* update shared Mic In / Center/LFE Out */ + shared = is_shared_clfeout(ac97); /* misc control; vrefout disable */ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, shared ? (1 << 12) : 0); @@ -2301,16 +2315,16 @@ static void alc850_update_jacks(struct snd_ac97 *ac97) { int shared; - /* shared Line-In */ - shared = is_shared_linein(ac97); + /* shared Line-In / Surround Out */ + shared = is_shared_surrout(ac97); /* SURR 1kOhm (bit4), Amp (bit5) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5), shared ? (1<<5) : (1<<4)); /* LINE-IN = 0, SURROUND = 2 */ snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12, shared ? (2<<12) : (0<<12)); - /* update shared mic */ - shared = is_shared_micin(ac97); + /* update shared Mic In / Center/LFE Out */ + shared = is_shared_clfeout(ac97); /* Vref disable (bit12), 1kOhm (bit13) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13), shared ? (1<<12) : (1<<13)); @@ -2383,9 +2397,9 @@ int patch_alc850(struct snd_ac97 *ac97) */ static void cm9738_update_jacks(struct snd_ac97 *ac97) { - /* shared Line-In */ + /* shared Line-In / Surround Out */ snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10, - is_shared_linein(ac97) ? (1 << 10) : 0); + is_shared_surrout(ac97) ? (1 << 10) : 0); } static const struct snd_kcontrol_new snd_ac97_cm9738_controls[] = { @@ -2467,12 +2481,12 @@ static const struct snd_kcontrol_new snd_ac97_cm9739_controls_spdif[] = { static void cm9739_update_jacks(struct snd_ac97 *ac97) { - /* shared Line-In */ + /* shared Line-In / Surround Out */ snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10, - is_shared_linein(ac97) ? (1 << 10) : 0); - /* shared Mic */ + is_shared_surrout(ac97) ? (1 << 10) : 0); + /* shared Mic In / Center/LFE Out **/ snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000, - is_shared_micin(ac97) ? 0x1000 : 0x2000); + is_shared_clfeout(ac97) ? 0x1000 : 0x2000); } static const struct snd_kcontrol_new snd_ac97_cm9739_controls[] = { @@ -2584,8 +2598,8 @@ static void cm9761_update_jacks(struct snd_ac97 *ac97) val |= surr_on[ac97->spec.dev_flags][is_surround_on(ac97)]; val |= clfe_on[ac97->spec.dev_flags][is_clfe_on(ac97)]; - val |= surr_shared[ac97->spec.dev_flags][is_shared_linein(ac97)]; - val |= clfe_shared[ac97->spec.dev_flags][is_shared_micin(ac97)]; + val |= surr_shared[ac97->spec.dev_flags][is_shared_surrout(ac97)]; + val |= clfe_shared[ac97->spec.dev_flags][is_shared_clfeout(ac97)]; snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3c88, val); } @@ -2832,12 +2846,12 @@ int patch_vt1617a(struct snd_ac97 * ac97) */ static void it2646_update_jacks(struct snd_ac97 *ac97) { - /* shared Line-In */ + /* shared Line-In / Surround Out */ snd_ac97_update_bits(ac97, 0x76, 1 << 9, - is_shared_linein(ac97) ? (1<<9) : 0); - /* shared Mic */ + is_shared_surrout(ac97) ? (1<<9) : 0); + /* shared Mic / Center/LFE Out */ snd_ac97_update_bits(ac97, 0x76, 1 << 10, - is_shared_micin(ac97) ? (1<<10) : 0); + is_shared_clfeout(ac97) ? (1<<10) : 0); } static const struct snd_kcontrol_new snd_ac97_controls_it2646[] = { -- cgit v1.2.3-59-g8ed1b From 1321b160fa1cf63fa841d954fe31220366b6647a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 21 Dec 2006 11:02:06 +0100 Subject: [ALSA] soc - Fix delayed_work related changes on 2.6.20 kernel Fix the changes realted to delayed_work in soc/codecs/wm8750.c. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 1 + sound/soc/codecs/wm8750.c | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index c985a111bc3f..ea836d819ce0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -374,6 +374,7 @@ struct snd_soc_codec { struct list_head dapm_paths; unsigned int dapm_state; unsigned int suspend_dapm_state; + struct delayed_work delayed_work; /* codec DAI's */ struct snd_soc_codec_dai *dai; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 069b66cb18e0..e7f04b89c8a0 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -51,8 +51,6 @@ #define warn(format, arg...) \ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) -static struct work_struct wm8750_dapm_work; - /* * wm8750 register cache * We can't read the WM8750 register space when we @@ -1000,9 +998,10 @@ struct snd_soc_codec_dai wm8750_dai = { }; EXPORT_SYMBOL_GPL(wm8750_dai); -static void wm8750_work(void *data) +static void wm8750_work(struct work_struct *work) { - struct snd_soc_codec *codec = (struct snd_soc_codec *)data; + struct snd_soc_codec *codec = + container_of(work, struct snd_soc_codec, delayed_work.work); wm8750_dapm_event(codec, codec->dapm_state); } @@ -1038,7 +1037,7 @@ static int wm8750_resume(struct platform_device *pdev) if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D0; - schedule_delayed_work(&wm8750_dapm_work, + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); } @@ -1083,7 +1082,7 @@ static int wm8750_init(struct snd_soc_device *socdev) /* charge output caps */ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D3hot; - schedule_delayed_work(&wm8750_dapm_work, msecs_to_jiffies(1000)); + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); /* set the update bits */ reg = wm8750_read_reg_cache(codec, WM8750_LDAC); @@ -1225,7 +1224,7 @@ static int wm8750_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); wm8750_socdev = socdev; - INIT_WORK(&wm8750_dapm_work, wm8750_work, codec); + INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) if (setup->i2c_address) { normal_i2c[0] = setup->i2c_address; -- cgit v1.2.3-59-g8ed1b From 6428ea1b733e4795209ff272be32732ec152594a Mon Sep 17 00:00:00 2001 From: Randy Cushman Date: Thu, 21 Dec 2006 19:17:29 +0100 Subject: [ALSA] ac97 - fix malfunctioning mixer controls for AD1985 This patch replaces the 'V_REFOUT Enable' mixer switch control with a listbox control for the AD1985 CODEC. Previous patch 'AD1888 mixer controls for DC mode' added controls that were propogated to multiple codecs. For the AD1985 codec, the bits VREFH and VREFD function differently, preventing the 'V_REFOUT Enable' control from setting V_REFOUT to Hi-Z. This patch also corrects an issue in which register bits relating to mixer controls 'Surround Jack Mode' and 'Channel Mode'. The register bits controlled by these controls were being set at boot time to states inconsistent with the stored values of these controls. Signed-off-by: Randy Cushman Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 103 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 5f69b9c9f1b3..bd27531a0f0e 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1632,13 +1632,16 @@ int patch_ad1886(struct snd_ac97 * ac97) #define AC97_AD198X_MBC_10 0x0001 /* +10dB */ #define AC97_AD198X_MBC_30 0x0002 /* +30dB */ #define AC97_AD198X_VREFD 0x0004 /* VREF high-Z */ -#define AC97_AD198X_VREFH 0x0008 /* 2.25V, 3.7V */ -#define AC97_AD198X_VREF_0 0x000c /* 0V */ +#define AC97_AD198X_VREFH 0x0008 /* 0=2.25V, 1=3.7V */ +#define AC97_AD198X_VREF_0 0x000c /* 0V (AD1985 only) */ +#define AC97_AD198X_VREF_MASK (AC97_AD198X_VREFH | AC97_AD198X_VREFD) +#define AC97_AD198X_VREF_SHIFT 2 #define AC97_AD198X_SRU 0x0010 /* sample rate unlock */ #define AC97_AD198X_LOSEL 0x0020 /* LINE_OUT amplifiers input select */ #define AC97_AD198X_2MIC 0x0040 /* 2-channel mic select */ #define AC97_AD198X_SPRD 0x0080 /* SPREAD enable */ -#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: 0 = 6-to-4, 1 = 6-to-2 downmix */ +#define AC97_AD198X_DMIX0 0x0100 /* downmix mode: */ + /* 0 = 6-to-4, 1 = 6-to-2 downmix */ #define AC97_AD198X_DMIX1 0x0200 /* downmix mode: 1 = enabled */ #define AC97_AD198X_HPSEL 0x0400 /* headphone amplifier input select */ #define AC97_AD198X_CLDIS 0x0800 /* center/lfe disable */ @@ -1969,8 +1972,80 @@ int patch_ad1980(struct snd_ac97 * ac97) return 0; } +static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + static const int reg2ctrl[4] = {2, 0, 1, 3}; + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + val = (ac97->regs[AC97_AD_MISC] & AC97_AD198X_VREF_MASK) + >> AC97_AD198X_VREF_SHIFT; + ucontrol->value.enumerated.item[0] = reg2ctrl[val]; + return 0; +} + +static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + static const int ctrl2reg[4] = {1, 2, 0, 3}; + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 3 + || ucontrol->value.enumerated.item[0] < 0) + return -EINVAL; + val = ctrl2reg[ucontrol->value.enumerated.item[0]] + << AC97_AD198X_VREF_SHIFT; + return snd_ac97_update_bits(ac97, AC97_AD_MISC, + AC97_AD198X_VREF_MASK, val); +} + static const struct snd_kcontrol_new snd_ac97_ad1985_controls[] = { - AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0) + AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Exchange Front/Surround", + .info = snd_ac97_ad1888_lohpsel_info, + .get = snd_ac97_ad1888_lohpsel_get, + .put = snd_ac97_ad1888_lohpsel_put + }, + AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1), + AC97_SINGLE("Spread Front to Surround and Center/LFE", + AC97_AD_MISC, 7, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Downmix", + .info = snd_ac97_ad1888_downmix_info, + .get = snd_ac97_ad1888_downmix_get, + .put = snd_ac97_ad1888_downmix_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "V_REFOUT", + .info = snd_ac97_ad1985_vrefout_info, + .get = snd_ac97_ad1985_vrefout_get, + .put = snd_ac97_ad1985_vrefout_put + }, + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, + + AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0), + AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0), }; static void ad1985_update_jacks(struct snd_ac97 *ac97) @@ -1984,9 +2059,16 @@ static int patch_ad1985_specific(struct snd_ac97 *ac97) { int err; - if ((err = patch_ad1980_specific(ac97)) < 0) + /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ + snd_ac97_rename_vol_ctl(ac97, "Master Playback", + "Master Surround Playback"); + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); + + if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0) return err; - return patch_build_controls(ac97, snd_ac97_ad1985_controls, ARRAY_SIZE(snd_ac97_ad1985_controls)); + + return patch_build_controls(ac97, snd_ac97_ad1985_controls, + ARRAY_SIZE(snd_ac97_ad1985_controls)); } static struct snd_ac97_build_ops patch_ad1985_build_ops = { @@ -2006,19 +2088,18 @@ int patch_ad1985(struct snd_ac97 * ac97) ac97->build_ops = &patch_ad1985_build_ops; misc = snd_ac97_read(ac97, AC97_AD_MISC); /* switch front/surround line-out/hp-out */ - /* center/LFE, mic in 3.75V mode */ /* AD-compatible mode */ /* Stereo mutes enabled */ - /* in accordance with ADI driver: misc | 0x5c28 */ snd_ac97_write_cache(ac97, AC97_AD_MISC, misc | - AC97_AD198X_VREFH | AC97_AD198X_LOSEL | AC97_AD198X_HPSEL | - AC97_AD198X_CLDIS | - AC97_AD198X_LODIS | AC97_AD198X_MSPLT | AC97_AD198X_AC97NC); ac97->flags |= AC97_STEREO_MUTES; + + /* update current jack configuration */ + ad1985_update_jacks(ac97); + /* on AD1985 rev. 3, AC'97 revision bits are zero */ ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23; return 0; -- cgit v1.2.3-59-g8ed1b From 67e9f4b68c9d1820132c559c0f9b296dafdf631e Mon Sep 17 00:00:00 2001 From: Randy Cushman Date: Fri, 22 Dec 2006 12:44:25 +0100 Subject: [ALSA] ac97 - fix various issues with AD1986/AD1986A support Previously, ac97_codec.c was coded to support AD1986 and AD1986A CODECs using code written for the AD1985 CODEC. This allowed the LINE_OUT and HEADPHONE jacks to function properly, however register differences between the CODECs prevented line and microphone inputs from functioning. Specifically, this patch fixes issues with the following mixer controls: 'V_REFOUT', 'Spread Front to Surround and Center/LFE', 'Exchange Front/Surround', 'Surround Jack Mode', and 'Channel Mode'. This patch removes the undocumented AD1888 control 'High Pass Filter Enable' and adds the new control 'Exchange Mic/Line In'. Signed-off-by: Randy Cushman Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ac97_codec.h | 1 + sound/pci/ac97/ac97_codec.c | 2 +- sound/pci/ac97/ac97_patch.c | 367 +++++++++++++++++++++++++++++++++++++++++++- sound/pci/ac97/ac97_patch.h | 1 + 4 files changed, 369 insertions(+), 2 deletions(-) diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 5d3f0d8c0e61..246ac23534bd 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -503,6 +503,7 @@ struct snd_ac97 { unsigned short id[3]; // codec IDs (lower 16-bit word) unsigned short pcmreg[3]; // PCM registers unsigned short codec_cfg[3]; // CODEC_CFG bits + unsigned char swap_mic_linein; // AD1986/AD1986A only } ad18xx; unsigned int dev_flags; /* device specific */ } spec; diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 9da4977c0a0c..bd8cfdcfbdf1 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -111,7 +111,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { { 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL }, { 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL }, { 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL }, -{ 0x41445378, 0xffffffff, "AD1986", patch_ad1985, NULL }, +{ 0x41445378, 0xffffffff, "AD1986", patch_ad1986, NULL }, { 0x414c4300, 0xffffff00, "ALC100,100P", NULL, NULL }, { 0x414c4710, 0xfffffff0, "ALC200,200P", NULL, NULL }, { 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */ diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index bd27531a0f0e..f5b4b44bbdab 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1626,7 +1626,7 @@ int patch_ad1886(struct snd_ac97 * ac97) return 0; } -/* MISC bits */ +/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */ #define AC97_AD198X_MBC 0x0003 /* mic boost */ #define AC97_AD198X_MBC_20 0x0000 /* +20dB */ #define AC97_AD198X_MBC_10 0x0001 /* +10dB */ @@ -1650,6 +1650,83 @@ int patch_ad1886(struct snd_ac97 * ac97) #define AC97_AD198X_AC97NC 0x4000 /* AC97 no compatible mode */ #define AC97_AD198X_DACZ 0x8000 /* DAC zero-fill mode */ +/* MISC 1 bits (AD1986 register 0x76) */ +#define AC97_AD1986_MBC 0x0003 /* mic boost */ +#define AC97_AD1986_MBC_20 0x0000 /* +20dB */ +#define AC97_AD1986_MBC_10 0x0001 /* +10dB */ +#define AC97_AD1986_MBC_30 0x0002 /* +30dB */ +#define AC97_AD1986_LISEL0 0x0004 /* LINE_IN select bit 0 */ +#define AC97_AD1986_LISEL1 0x0008 /* LINE_IN select bit 1 */ +#define AC97_AD1986_LISEL_MASK (AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0) +#define AC97_AD1986_LISEL_LI 0x0000 /* LINE_IN pins as LINE_IN source */ +#define AC97_AD1986_LISEL_SURR 0x0004 /* SURROUND pins as LINE_IN source */ +#define AC97_AD1986_LISEL_MIC 0x0008 /* MIC_1/2 pins as LINE_IN source */ +#define AC97_AD1986_SRU 0x0010 /* sample rate unlock */ +#define AC97_AD1986_SOSEL 0x0020 /* SURROUND_OUT amplifiers input sel */ +#define AC97_AD1986_2MIC 0x0040 /* 2-channel mic select */ +#define AC97_AD1986_SPRD 0x0080 /* SPREAD enable */ +#define AC97_AD1986_DMIX0 0x0100 /* downmix mode: */ + /* 0 = 6-to-4, 1 = 6-to-2 downmix */ +#define AC97_AD1986_DMIX1 0x0200 /* downmix mode: 1 = enabled */ +#define AC97_AD1986_CLDIS 0x0800 /* center/lfe disable */ +#define AC97_AD1986_SODIS 0x1000 /* SURROUND_OUT disable */ +#define AC97_AD1986_MSPLT 0x2000 /* mute split (read only 1) */ +#define AC97_AD1986_AC97NC 0x4000 /* AC97 no compatible mode (r/o 1) */ +#define AC97_AD1986_DACZ 0x8000 /* DAC zero-fill mode */ + +/* MISC 2 bits (AD1986 register 0x70) */ +#define AC97_AD_MISC2 0x70 /* Misc Control Bits 2 (AD1986) */ + +#define AC97_AD1986_CVREF0 0x0004 /* C/LFE VREF_OUT 2.25V */ +#define AC97_AD1986_CVREF1 0x0008 /* C/LFE VREF_OUT 0V */ +#define AC97_AD1986_CVREF2 0x0010 /* C/LFE VREF_OUT 3.7V */ +#define AC97_AD1986_CVREF_MASK \ + (AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0) +#define AC97_AD1986_JSMAP 0x0020 /* Jack Sense Mapping 1 = alternate */ +#define AC97_AD1986_MMDIS 0x0080 /* Mono Mute Disable */ +#define AC97_AD1986_MVREF0 0x0400 /* MIC VREF_OUT 2.25V */ +#define AC97_AD1986_MVREF1 0x0800 /* MIC VREF_OUT 0V */ +#define AC97_AD1986_MVREF2 0x1000 /* MIC VREF_OUT 3.7V */ +#define AC97_AD1986_MVREF_MASK \ + (AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0) + +/* MISC 3 bits (AD1986 register 0x7a) */ +#define AC97_AD_MISC3 0x7a /* Misc Control Bits 3 (AD1986) */ + +#define AC97_AD1986_MMIX 0x0004 /* Mic Mix, left/right */ +#define AC97_AD1986_GPO 0x0008 /* General Purpose Out */ +#define AC97_AD1986_LOHPEN 0x0010 /* LINE_OUT headphone drive */ +#define AC97_AD1986_LVREF0 0x0100 /* LINE_OUT VREF_OUT 2.25V */ +#define AC97_AD1986_LVREF1 0x0200 /* LINE_OUT VREF_OUT 0V */ +#define AC97_AD1986_LVREF2 0x0400 /* LINE_OUT VREF_OUT 3.7V */ +#define AC97_AD1986_LVREF_MASK \ + (AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0) +#define AC97_AD1986_JSINVA 0x0800 /* Jack Sense Invert SENSE_A */ +#define AC97_AD1986_LOSEL 0x1000 /* LINE_OUT amplifiers input select */ +#define AC97_AD1986_HPSEL0 0x2000 /* Headphone amplifiers */ + /* input select Surround DACs */ +#define AC97_AD1986_HPSEL1 0x4000 /* Headphone amplifiers input */ + /* select C/LFE DACs */ +#define AC97_AD1986_JSINVB 0x8000 /* Jack Sense Invert SENSE_B */ + +/* Serial Config bits (AD1986 register 0x74) (incomplete) */ +#define AC97_AD1986_OMS0 0x0100 /* Optional Mic Selector bit 0 */ +#define AC97_AD1986_OMS1 0x0200 /* Optional Mic Selector bit 1 */ +#define AC97_AD1986_OMS2 0x0400 /* Optional Mic Selector bit 2 */ +#define AC97_AD1986_OMS_MASK \ + (AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0) +#define AC97_AD1986_OMS_M 0x0000 /* MIC_1/2 pins are MIC sources */ +#define AC97_AD1986_OMS_L 0x0100 /* LINE_IN pins are MIC sources */ +#define AC97_AD1986_OMS_C 0x0200 /* Center/LFE pins are MCI sources */ +#define AC97_AD1986_OMS_MC 0x0400 /* Mix of MIC and C/LFE pins */ + /* are MIC sources */ +#define AC97_AD1986_OMS_ML 0x0500 /* MIX of MIC and LINE_IN pins */ + /* are MIC sources */ +#define AC97_AD1986_OMS_LC 0x0600 /* MIX of LINE_IN and C/LFE pins */ + /* are MIC sources */ +#define AC97_AD1986_OMS_MLC 0x0700 /* MIX of MIC, LINE_IN, C/LFE pins */ + /* are MIC sources */ + static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2105,6 +2182,294 @@ int patch_ad1985(struct snd_ac97 * ac97) return 0; } +static int snd_ac97_ad1986_bool_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 snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_AD_MISC3]; + ucontrol->value.integer.value[0] = (val & AC97_AD1986_LOSEL) != 0; + return 0; +} + +static int snd_ac97_ad1986_lososel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + int ret0; + int ret1; + int sprd = (ac97->regs[AC97_AD_MISC] & AC97_AD1986_SPRD) != 0; + + ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC3, AC97_AD1986_LOSEL, + ucontrol->value.integer.value[0] != 0 + ? AC97_AD1986_LOSEL : 0); + if (ret0 < 0) + return ret0; + + /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */ + ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL, + (ucontrol->value.integer.value[0] != 0 + || sprd) + ? AC97_AD1986_SOSEL : 0); + if (ret1 < 0) + return ret1; + + return (ret0 > 0 || ret1 > 0) ? 1 : 0; +} + +static int snd_ac97_ad1986_spread_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_AD_MISC]; + ucontrol->value.integer.value[0] = (val & AC97_AD1986_SPRD) != 0; + return 0; +} + +static int snd_ac97_ad1986_spread_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + int ret0; + int ret1; + int sprd = (ac97->regs[AC97_AD_MISC3] & AC97_AD1986_LOSEL) != 0; + + ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SPRD, + ucontrol->value.integer.value[0] != 0 + ? AC97_AD1986_SPRD : 0); + if (ret0 < 0) + return ret0; + + /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */ + ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL, + (ucontrol->value.integer.value[0] != 0 + || sprd) + ? AC97_AD1986_SOSEL : 0); + if (ret1 < 0) + return ret1; + + return (ret0 > 0 || ret1 > 0) ? 1 : 0; +} + +static int snd_ac97_ad1986_miclisel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = ac97->spec.ad18xx.swap_mic_linein; + return 0; +} + +static int snd_ac97_ad1986_miclisel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned char swap = ucontrol->value.integer.value[0] != 0; + + if (swap != ac97->spec.ad18xx.swap_mic_linein) { + ac97->spec.ad18xx.swap_mic_linein = swap; + if (ac97->build_ops->update_jacks) + ac97->build_ops->update_jacks(ac97); + return 1; + } + return 0; +} + +static int snd_ac97_ad1986_vrefout_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* Use MIC_1/2 V_REFOUT as the "get" value */ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + unsigned short reg = ac97->regs[AC97_AD_MISC2]; + if ((reg & AC97_AD1986_MVREF0) != 0) + val = 2; + else if ((reg & AC97_AD1986_MVREF1) != 0) + val = 3; + else if ((reg & AC97_AD1986_MVREF2) != 0) + val = 1; + else + val = 0; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_ac97_ad1986_vrefout_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short cval; + unsigned short lval; + unsigned short mval; + int cret; + int lret; + int mret; + + switch (ucontrol->value.enumerated.item[0]) + { + case 0: /* High-Z */ + cval = 0; + lval = 0; + mval = 0; + break; + case 1: /* 3.7 V */ + cval = AC97_AD1986_CVREF2; + lval = AC97_AD1986_LVREF2; + mval = AC97_AD1986_MVREF2; + break; + case 2: /* 2.25 V */ + cval = AC97_AD1986_CVREF0; + lval = AC97_AD1986_LVREF0; + mval = AC97_AD1986_MVREF0; + break; + case 3: /* 0 V */ + cval = AC97_AD1986_CVREF1; + lval = AC97_AD1986_LVREF1; + mval = AC97_AD1986_MVREF1; + break; + default: + return -EINVAL; + } + + cret = snd_ac97_update_bits(ac97, AC97_AD_MISC2, + AC97_AD1986_CVREF_MASK, cval); + if (cret < 0) + return cret; + lret = snd_ac97_update_bits(ac97, AC97_AD_MISC3, + AC97_AD1986_LVREF_MASK, lval); + if (lret < 0) + return lret; + mret = snd_ac97_update_bits(ac97, AC97_AD_MISC2, + AC97_AD1986_MVREF_MASK, mval); + if (mret < 0) + return mret; + + return (cret > 0 || lret > 0 || mret > 0) ? 1 : 0; +} + +static const struct snd_kcontrol_new snd_ac97_ad1986_controls[] = { + AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Exchange Front/Surround", + .info = snd_ac97_ad1986_bool_info, + .get = snd_ac97_ad1986_lososel_get, + .put = snd_ac97_ad1986_lososel_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Exchange Mic/Line In", + .info = snd_ac97_ad1986_bool_info, + .get = snd_ac97_ad1986_miclisel_get, + .put = snd_ac97_ad1986_miclisel_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Spread Front to Surround and Center/LFE", + .info = snd_ac97_ad1986_bool_info, + .get = snd_ac97_ad1986_spread_get, + .put = snd_ac97_ad1986_spread_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Downmix", + .info = snd_ac97_ad1888_downmix_info, + .get = snd_ac97_ad1888_downmix_get, + .put = snd_ac97_ad1888_downmix_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "V_REFOUT", + .info = snd_ac97_ad1985_vrefout_info, + .get = snd_ac97_ad1986_vrefout_get, + .put = snd_ac97_ad1986_vrefout_put + }, + AC97_SURROUND_JACK_MODE_CTL, + AC97_CHANNEL_MODE_CTL, + + AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0), + AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0) +}; + +static void ad1986_update_jacks(struct snd_ac97 *ac97) +{ + unsigned short misc_val = 0; + unsigned short ser_val; + + /* disable SURROUND and CENTER/LFE if not surround mode */ + if (! is_surround_on(ac97)) + misc_val |= AC97_AD1986_SODIS; + if (! is_clfe_on(ac97)) + misc_val |= AC97_AD1986_CLDIS; + + /* select line input (default=LINE_IN, SURROUND or MIC_1/2) */ + if (is_shared_linein(ac97)) + misc_val |= AC97_AD1986_LISEL_SURR; + else if (ac97->spec.ad18xx.swap_mic_linein != 0) + misc_val |= AC97_AD1986_LISEL_MIC; + snd_ac97_update_bits(ac97, AC97_AD_MISC, + AC97_AD1986_SODIS | AC97_AD1986_CLDIS | + AC97_AD1986_LISEL_MASK, + misc_val); + + /* select microphone input (MIC_1/2, Center/LFE or LINE_IN) */ + if (is_shared_micin(ac97)) + ser_val = AC97_AD1986_OMS_C; + else if (ac97->spec.ad18xx.swap_mic_linein != 0) + ser_val = AC97_AD1986_OMS_L; + else + ser_val = AC97_AD1986_OMS_M; + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, + AC97_AD1986_OMS_MASK, + ser_val); +} + +static int patch_ad1986_specific(struct snd_ac97 *ac97) +{ + int err; + + if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0) + return err; + + return patch_build_controls(ac97, snd_ac97_ad1986_controls, + ARRAY_SIZE(snd_ac97_ad1985_controls)); +} + +static struct snd_ac97_build_ops patch_ad1986_build_ops = { + .build_post_spdif = patch_ad198x_post_spdif, + .build_specific = patch_ad1986_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume, +#endif + .update_jacks = ad1986_update_jacks, +}; + +int patch_ad1986(struct snd_ac97 * ac97) +{ + patch_ad1881(ac97); + ac97->build_ops = &patch_ad1986_build_ops; + ac97->flags |= AC97_STEREO_MUTES; + + /* update current jack configuration */ + ad1986_update_jacks(ac97); + + return 0; +} + + /* * realtek ALC65x/850 codecs */ diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 741979217207..94340daaaf1f 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -48,6 +48,7 @@ int patch_ad1980(struct snd_ac97 * ac97); int patch_ad1981a(struct snd_ac97 * ac97); int patch_ad1981b(struct snd_ac97 * ac97); int patch_ad1985(struct snd_ac97 * ac97); +int patch_ad1986(struct snd_ac97 * ac97); int patch_alc650(struct snd_ac97 * ac97); int patch_alc655(struct snd_ac97 * ac97); int patch_alc850(struct snd_ac97 * ac97); -- cgit v1.2.3-59-g8ed1b From 88518275e3eefe0582af1918d59325b16dfde154 Mon Sep 17 00:00:00 2001 From: John Daiker Date: Thu, 28 Dec 2006 13:55:05 +0100 Subject: [ALSA] usbaudio.c: remove unneeded casts Went rummaging through usbaudio.c and found some castings that aren't needed as far as I can see. Part of the KernelJanitors TODO list. Signed-off-by: John Daiker Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index d2e066dc5d81..de680d095e94 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -253,7 +253,7 @@ static int prepare_capture_sync_urb(struct snd_usb_substream *subs, struct urb *urb) { unsigned char *cp = urb->transfer_buffer; - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; urb->dev = ctx->subs->dev; /* we need to set this at each time */ urb->iso_frame_desc[0].length = 3; @@ -275,7 +275,7 @@ static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs, struct urb *urb) { unsigned char *cp = urb->transfer_buffer; - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; urb->dev = ctx->subs->dev; /* we need to set this at each time */ urb->iso_frame_desc[0].length = 4; @@ -313,7 +313,7 @@ static int prepare_capture_urb(struct snd_usb_substream *subs, struct urb *urb) { int i, offs; - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; offs = 0; urb->dev = ctx->subs->dev; /* we need to set this at each time */ @@ -412,7 +412,7 @@ static int prepare_playback_sync_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) { - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; urb->dev = ctx->subs->dev; /* we need to set this at each time */ urb->iso_frame_desc[0].length = 3; @@ -430,7 +430,7 @@ static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) { - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; urb->dev = ctx->subs->dev; /* we need to set this at each time */ urb->iso_frame_desc[0].length = 4; @@ -547,7 +547,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, unsigned int counts; unsigned long flags; int period_elapsed = 0; - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; stride = runtime->frame_bits >> 3; @@ -665,7 +665,7 @@ static struct snd_urb_ops audio_urb_ops_high_speed[2] = { */ static void snd_complete_urb(struct urb *urb) { - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; struct snd_usb_substream *subs = ctx->subs; struct snd_pcm_substream *substream = ctx->subs->pcm_substream; int err = 0; @@ -688,7 +688,7 @@ static void snd_complete_urb(struct urb *urb) */ static void snd_complete_sync_urb(struct urb *urb) { - struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context; + struct snd_urb_ctx *ctx = urb->context; struct snd_usb_substream *subs = ctx->subs; struct snd_pcm_substream *substream = ctx->subs->pcm_substream; int err = 0; @@ -1429,7 +1429,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data; + struct snd_usb_substream *subs = substream->runtime->private_data; struct audioformat *fmt; unsigned int channels, rate, format; int ret, changed; @@ -1485,7 +1485,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, */ static int snd_usb_hw_free(struct snd_pcm_substream *substream) { - struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data; + struct snd_usb_substream *subs = substream->runtime->private_data; subs->cur_audiofmt = NULL; subs->cur_rate = 0; -- cgit v1.2.3-59-g8ed1b From 518f6a6173e6009f5d380c638ea97a8897ebea9c Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Thu, 28 Dec 2006 13:55:41 +0100 Subject: [ALSA] Fix typo and add entry to documentation This patch adds the macpro and fixes a typo in the ALC882 section of ALSA-Configuration.txt. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 89b612f835ad..14debd58c182 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -814,8 +814,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ALC882/885 3stack-dig 3-jack with SPDIF I/O - 6stck-dig 6-jack digital with SPDIF I/O + 6stack-dig 6-jack digital with SPDIF I/O arima Arima W820Di1 + macpro MacPro support auto auto-config reading BIOS (default) ALC883/888 -- cgit v1.2.3-59-g8ed1b From 7b9470d88492d8be22d1f5307fe28642db9affe5 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Thu, 28 Dec 2006 13:56:48 +0100 Subject: [ALSA] hda-codec - Add Asus P5W DH to alc882_cfg_tbl This patch adds the Asus P5W DH to the ALC882 config table as a 6stack-dig system. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2be0ef9023b1..e4e7512ed02a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4738,6 +4738,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), + SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), {} }; -- cgit v1.2.3-59-g8ed1b From f6cdab5f7ed356e8a259c1f00c7991f56c234643 Mon Sep 17 00:00:00 2001 From: Clement Guedez Date: Mon, 8 Jan 2007 10:48:41 +0100 Subject: [ALSA] Add support of the ESI Waveterminal 192M to the ice1724 ALSA driver This patch adds the support of the ESI Waveterminal 192M soundcard to the ice1724 familly ALSA driver. It's a semi-professionnal soundcard for home studio : many I/O and a quality of sound is good, better than consumer cards, but less musical than professional cards. It use a Via Envy24ht chipset as ice1724 soundcard, Sigmatel stac9640 ADC/DAC for the analog I/O as Prodigy192, and Atmel ak4114 for S/PDIF as ESI Julia. Is working : the 8 analog outputs, the analog inputs 1&2, the mic input 1, the coaxial & optical digital outputs. Signed-off-by: Clement Guedez Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ice1712/Makefile | 2 +- sound/pci/ice1712/ice1724.c | 4 +- sound/pci/ice1712/wtm.c | 542 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/ice1712/wtm.h | 20 ++ 4 files changed, 566 insertions(+), 2 deletions(-) create mode 100644 sound/pci/ice1712/wtm.c create mode 100644 sound/pci/ice1712/wtm.h diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 7837cef8855c..6efdd62f6837 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 juli.o phase.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.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 3e3a102e6c34..4566c0f789aa 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -50,7 +50,7 @@ #include "prodigy192.h" #include "juli.h" #include "phase.h" - +#include "wtm.h" MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); @@ -64,6 +64,7 @@ MODULE_SUPPORTED_DEVICE("{" PRODIGY192_DEVICE_DESC JULI_DEVICE_DESC PHASE_DEVICE_DESC + WTM_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -1958,6 +1959,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_prodigy192_cards, snd_vt1724_juli_cards, snd_vt1724_phase_cards, + snd_vt1724_wtm_cards, NULL, }; diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c new file mode 100644 index 000000000000..04e535c8542b --- /dev/null +++ b/sound/pci/ice1712/wtm.c @@ -0,0 +1,542 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Ego Sys Waveterminal 192M + * + * Copyright (c) 2006 Guedez Clement + * Some functions are taken from the Prodigy192 driver + * source + * + * 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 "ice1712.h" +#include "envy24ht.h" +#include "wtm.h" +#include "stac946x.h" + + +/* + * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus + */ +static inline void stac9460_put(struct snd_ice1712 *ice, int reg, + unsigned char val) +{ + snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val); +} + +static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg) +{ + return snd_vt1724_read_i2c(ice, STAC9460_I2C_ADDR, reg); +} + +/* + * 2*ADC 2*DAC no2 ringbuffer r/w on i2c bus + */ +static inline void stac9460_2_put(struct snd_ice1712 *ice, int reg, + unsigned char val) +{ + snd_vt1724_write_i2c(ice, STAC9460_2_I2C_ADDR, reg, val); +} + +static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg) +{ + return snd_vt1724_read_i2c(ice, STAC9460_2_I2C_ADDR, reg); +} + + +/* + * DAC mute control + */ +static int stac9460_dac_mute_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; + return 0; +} + +static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char val; + int idx, id; + + if (kcontrol->private_value) { + idx = STAC946X_MASTER_VOLUME; + id = 0; + } else { + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + idx = id + STAC946X_LF_VOLUME; + } + if (id < 6) + val = stac9460_get(ice, idx); + else + val = stac9460_2_get(ice,idx - 6); + ucontrol->value.integer.value[0] = (~val >> 7) & 0x1; + return 0; +} + +static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char new, old; + int id, idx; + int change; + + if (kcontrol->private_value) { + idx = STAC946X_MASTER_VOLUME; + old = stac9460_get(ice, idx); + new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | + (old & ~0x80); + change = (new != old); + if (change) { + stac9460_put(ice, idx, new); + stac9460_2_put(ice, idx, new); + } + } else { + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + idx = id + STAC946X_LF_VOLUME; + if (id < 6) + old = stac9460_get(ice, idx); + else + old = stac9460_2_get(ice, idx - 6); + new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | + (old & ~0x80); + change = (new != old); + if (change) { + if (id < 6) + stac9460_put(ice, idx, new); + else + stac9460_2_put(ice, idx - 6, new); + } + } + return change; +} + +/* + * DAC volume attenuation mixer control + */ +static int stac9460_dac_vol_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; /* mute */ + uinfo->value.integer.max = 0x7f; /* 0dB */ + return 0; +} + +static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int idx, id; + unsigned char vol; + + if (kcontrol->private_value) { + idx = STAC946X_MASTER_VOLUME; + id = 0; + } else { + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + idx = id + STAC946X_LF_VOLUME; + } + if (id < 6) + vol = stac9460_get(ice, idx) & 0x7f; + else + vol = stac9460_2_get(ice, idx - 6) & 0x7f; + ucontrol->value.integer.value[0] = 0x7f - vol; + return 0; +} + +static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int idx, id; + unsigned char tmp, ovol, nvol; + int change; + + if (kcontrol->private_value) { + idx = STAC946X_MASTER_VOLUME; + nvol = ucontrol->value.integer.value[0]; + tmp = stac9460_get(ice, idx); + ovol = 0x7f - (tmp & 0x7f); + change = (ovol != nvol); + if (change) { + stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); + stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); + } + } else { + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + idx = id + STAC946X_LF_VOLUME; + nvol = ucontrol->value.integer.value[0]; + if (id < 6) + tmp = stac9460_get(ice, idx); + else + tmp = stac9460_2_get(ice, idx - 6); + ovol = 0x7f - (tmp & 0x7f); + change = (ovol != nvol); + if (change) { + if (id < 6) + stac9460_put(ice, idx, (0x7f - nvol) | + (tmp & 0x80)); + else + stac9460_2_put(ice, idx-6, (0x7f - nvol) | + (tmp & 0x80)); + } + } + return change; +} + +/* + * ADC mute control + */ +static int stac9460_adc_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char val; + int i, id; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) { + for (i = 0; i < 2; ++i) { + val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i); + ucontrol->value.integer.value[i] = ~val>>7 & 0x1; + } + } else { + for (i = 0; i < 2; ++i) { + val = stac9460_2_get(ice, STAC946X_MIC_L_VOLUME + i); + ucontrol->value.integer.value[i] = ~val>>7 & 0x1; + } + } + return 0; +} + +static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char new, old; + int i, reg, id; + int change; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + old = stac9460_get(ice, reg); + new = (~ucontrol->value.integer.value[i]<<7&0x80) | + (old&~0x80); + change = (new != old); + if (change) + stac9460_put(ice, reg, new); + } + } else { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + old = stac9460_2_get(ice, reg); + new = (~ucontrol->value.integer.value[i]<<7&0x80) | + (old&~0x80); + change = (new != old); + if (change) + stac9460_2_put(ice, reg, new); + } + } + return change; +} + +/* + *ADC gain mixer control + */ +static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* 0dB */ + uinfo->value.integer.max = 0x0f; /* 22.5dB */ + return 0; +} + +static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int i, reg, id; + unsigned char vol; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + vol = stac9460_get(ice, reg) & 0x0f; + ucontrol->value.integer.value[i] = 0x0f - vol; + } + } else { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + vol = stac9460_2_get(ice, reg) & 0x0f; + ucontrol->value.integer.value[i] = 0x0f - vol; + } + } + return 0; +} + +static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int i, reg, id; + unsigned char ovol, nvol; + int change; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + nvol = ucontrol->value.integer.value[i]; + ovol = 0x0f - stac9460_get(ice, reg); + change = ((ovol & 0x0f) != nvol); + if (change) + stac9460_put(ice, reg, (0x0f - nvol) | + (ovol & ~0x0f)); + } + } else { + for (i = 0; i < 2; ++i) { + reg = STAC946X_MIC_L_VOLUME + i; + nvol = ucontrol->value.integer.value[i]; + ovol = 0x0f - stac9460_2_get(ice, reg); + change = ((ovol & 0x0f) != nvol); + if (change) + stac9460_2_put(ice, reg, (0x0f - nvol) | + (ovol & ~0x0f)); + } + } + return change; +} + +/* + * MIC / LINE switch fonction + */ + +static int stac9460_mic_sw_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 stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char val; + int id; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) + val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); + else + val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); + ucontrol->value.integer.value[0] = ~val>>7 & 0x1; + return 0; +} + +static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned char new, old; + int change, id; + + id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (id == 0) + old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); + else + old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); + new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) { + if (id == 0) + stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new); + else + stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new); + } + return change; +} + +/* + * Control tabs + */ +static struct snd_kcontrol_new stac9640_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = stac9460_dac_mute_info, + .get = stac9460_dac_mute_get, + .put = stac9460_dac_mute_put, + .private_value = 1 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = stac9460_dac_vol_info, + .get = stac9460_dac_vol_get, + .put = stac9460_dac_vol_put, + .private_value = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "MIC/Line switch", + .count = 2, + .info = stac9460_mic_sw_info, + .get = stac9460_mic_sw_get, + .put = stac9460_mic_sw_put, + + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Switch", + .count = 8, + .info = stac9460_dac_mute_info, + .get = stac9460_dac_mute_get, + .put = stac9460_dac_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Volume", + .count = 8, + .info = stac9460_dac_vol_info, + .get = stac9460_dac_vol_get, + .put = stac9460_dac_vol_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Switch", + .count = 2, + .info = stac9460_adc_mute_info, + .get = stac9460_adc_mute_get, + .put = stac9460_adc_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Volume", + .count = 2, + .info = stac9460_adc_vol_info, + .get = stac9460_adc_vol_get, + .put = stac9460_adc_vol_put, + + } +}; + + + +/*INIT*/ +static int __devinit wtm_add_controls(struct snd_ice1712 *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(stac9640_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&stac9640_controls[i], ice)); + if (err < 0) + return err; + } + return 0; +} + +static int __devinit wtm_init(struct snd_ice1712 *ice) +{ + static unsigned short stac_inits_prodigy[] = { + STAC946X_RESET, 0, + (unsigned short)-1 + }; + unsigned short *p; + + /*WTM 192M*/ + ice->num_total_dacs = 8; + ice->num_total_adcs = 4; + ice->force_rdma1 = 1; + + /*initialize codec*/ + p = stac_inits_prodigy; + for (; *p != (unsigned short)-1; p += 2) { + stac9460_put(ice, p[0], p[1]); + stac9460_2_put(ice, p[0], p[1]); + } + return 0; +} + + +static unsigned char wtm_eeprom[] __devinitdata = { + 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */ + 0x80, /* ACLINK : I2S */ + 0xf8, /* I2S: vol; 96k, 24bit, 192k */ + 0xc1 /*SPDIF: out-en, spidf ext out*/, + 0x9f, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x7f, /* GPIO_DIR2 */ + 0x9f, /* GPIO_MASK */ + 0xff, /* GPIO_MASK1 */ + 0x7f, /* GPIO_MASK2 */ + 0x16, /* GPIO_STATE */ + 0x80, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + + +/*entry point*/ +struct snd_ice1712_card_info snd_vt1724_wtm_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_WTM, + .name = "ESI Waveterminal 192M", + .model = "WT192M", + .chip_init = wtm_init, + .build_controls = wtm_add_controls, + .eeprom_size = sizeof(wtm_eeprom), + .eeprom_data = wtm_eeprom, + }, + {} /*terminator*/ +}; diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h new file mode 100644 index 000000000000..03a394e442f1 --- /dev/null +++ b/sound/pci/ice1712/wtm.h @@ -0,0 +1,20 @@ +#ifndef __SOUND_WTM_H +#define __SOUND_WTM_H + +/* ID */ +#define WTM_DEVICE_DESC "{EGO SYS INC,WaveTerminal 192M}," +#define VT1724_SUBDEVICE_WTM 0x36495345 /* WT192M ver1.0 */ + +/* + *chip addresses on I2C bus + */ + +#define AK4114_ADDR 0x20 /*S/PDIF receiver*/ +#define STAC9460_I2C_ADDR 0x54 /* ADC*2 | DAC*6 */ +#define STAC9460_2_I2C_ADDR 0x56 /* ADC|DAC *2 */ + + +extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[]; + +#endif /* __SOUND_WTM_H */ + -- cgit v1.2.3-59-g8ed1b From 0e4ceb7507111c3910a0d7e19b498b1f6081afcb Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 8 Jan 2007 10:54:26 +0100 Subject: [ALSA] hda-codec - Change default config for Asus P5GD1 This patch changes the default configuration for the Asus P5GD1 motherboard from 5stack to asus, as reported by stelek on linuxquestions.org http://www.linuxquestions.org/questions/showthread.php?p=2556497#post2556497 Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e4e7512ed02a..dc01a6bab2b8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2382,7 +2382,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_5ST), + SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_ASUS), SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), -- cgit v1.2.3-59-g8ed1b From 751e61c47d3b4e929c93bac61c8dd6c247854993 Mon Sep 17 00:00:00 2001 From: Raúl Sánchez Siles Date: Mon, 8 Jan 2007 10:56:48 +0100 Subject: [ALSA] Solve typos/compilation problems for debug functions in soc-dapm and at91-i2s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit soc-dapm ·Removed list_for_each since the loop is list_for_each_entry() and not list_for_each(). Thanks to Liam Girdwood and Seth Forshee. at91-i2s ·Fixed typo in dai modes definition. ·Fixed struct member name in at91_ssc_info->ssc_state. ·Fixed compilation problem, ssc_state is bundled in at91_ssc_info. Signed-off-by: Raúl Sánchez Siles Signed-off-by: Seth Forshee Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91-i2s.c | 34 +++++++++++++++++----------------- sound/soc/soc-dapm.c | 1 - 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-i2s.c index b452e8e6a724..203e6948cd99 100644 --- a/sound/soc/at91/at91-i2s.c +++ b/sound/soc/at91/at91-i2s.c @@ -101,7 +101,7 @@ static struct snd_soc_dai_mode at91_i2s[] = { .pcmdir = AT91_I2S_DIR, .flags = SND_SOC_DAI_BFS_DIV, .fs = 250, - .bfs SND_SOC_FSBD(5), + .bfs = SND_SOC_FSBD(5), .priv = (13 << 16 | 23), }, }; @@ -352,19 +352,19 @@ static int at91_i2s_suspend(struct platform_device *pdev, ssc_p = &ssc_info[dai->id]; /* Save the status register before disabling transmit and receive. */ - ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); + ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); /* Save the current interrupt mask, then disable unmasked interrupts. */ - ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->state->ssc_imr); + ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr); - ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); - ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); + ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); return 0; } @@ -380,17 +380,17 @@ static int at91_i2s_resume(struct platform_device *pdev, ssc_p = &ssc_info[dai->id]; - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->state->ssc_cmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_tfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_tcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->state->ssc_imr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr); at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, - ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | - ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); + ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | + ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); return 0; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 411651dc9d1d..5c2a34956a5d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -651,7 +651,6 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) p->source->name); } list_for_each_entry(p, &w->sinks, list_source) { - p = list_entry(lp, struct snd_soc_dapm_path, list_source); if (p->connect) printk(" out %s %s\n", p->name ? p->name : "static", p->sink->name); -- cgit v1.2.3-59-g8ed1b From ad5e773750aeae3ad980f94b9f3cecad5af7c53d Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 8 Jan 2007 10:57:32 +0100 Subject: [ALSA] hda-codec - Add support for Toshiba M105 to Realtek patch This patch adds support for the Toshiba M105-S3041 laptop (ALC861). Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dc01a6bab2b8..468e9a09f44d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7592,6 +7592,7 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660_3ST), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), -- cgit v1.2.3-59-g8ed1b From ffc26918ab5674b286a4bc07dac7e011cea83e97 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Mon, 8 Jan 2007 10:58:47 +0100 Subject: [ALSA] ASoC at91 - Fix NULL pointer dereference in at91_i2s_shutdown This patch fixes a NULL pointer exception which occurs when a substream is opened and immediately closed. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91-i2s.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-i2s.c index 203e6948cd99..876d391c7013 100644 --- a/sound/soc/at91/at91-i2s.c +++ b/sound/soc/at91/at91-i2s.c @@ -296,6 +296,13 @@ static int at91_i2s_startup(struct snd_pcm_substream *substream) ssc_p->dir_mask |= dir_mask; spin_unlock_irq(&ssc_p->lock); + /* + * dma_data is not set until hw_params() is called and + * shutdown() depends on this value being NULL if hw_params() + * was not called. + */ + rtd->cpu_dai->dma_data = NULL; + return 0; } -- cgit v1.2.3-59-g8ed1b From c68487151a0dceea07db8632327e3a0ab9e25e1f Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Mon, 8 Jan 2007 10:59:51 +0100 Subject: [ALSA] sound: aoa of_node_put and kfree cleanup This patch removes redundant argument checks for of_node_put() and kfree(). Acked-by: Johannes Berg Signed-off-by: Mariusz Kozlowski Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/fabrics/snd-aoa-fabric-layout.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index 409809600ddc..fa4a69fcdc27 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -1034,9 +1034,9 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) list_del(&ldev->list); layouts_list_items--; outnodev: - if (sound) of_node_put(sound); + of_node_put(sound); layout_device = NULL; - if (ldev) kfree(ldev); + kfree(ldev); return -ENODEV; } -- cgit v1.2.3-59-g8ed1b From 8e21c34cd4742c508dcc307fdbac9b3ba6899002 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 8 Jan 2007 11:04:17 +0100 Subject: [ALSA] hda-codec - Add support for Sigmatel STAC9202/9250/9251 codecs This patch adds support for Gateway laptops based on the Sigmatel STAC9250 codecs, as well as basic support for STAC9202/9250/9251 codecs. Some Gateway systems require probe_mask=1 to work. More work to be done prior to alsa 1.0.14 final. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 5 + sound/pci/hda/patch_sigmatel.c | 140 ++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 14debd58c182..ce2f5faa930f 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -890,6 +890,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack D945 3stack 5stack D945 5stack + SPDIF + STAC9202/9250/9251 + ref Reference board, base config + m2-2 Some Gateway MX series laptops + m6 Some Gateway NX series laptops + STAC9227/9228/9229/927x ref Reference board 3stack D965 3stack diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4e3fc95b7b4f..0556b7e7bb8b 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -47,6 +47,13 @@ enum { STAC_9205_MODELS }; +enum { + STAC_925x_REF, + STAC_M2_2, + STAC_MA6, + STAC_925x_MODELS +}; + enum { STAC_D945_REF, STAC_D945GTP3, @@ -129,6 +136,18 @@ static hda_nid_t stac9200_dac_nids[1] = { 0x02, }; +static hda_nid_t stac925x_adc_nids[1] = { + 0x03, +}; + +static hda_nid_t stac925x_mux_nids[1] = { + 0x0f, +}; + +static hda_nid_t stac925x_dac_nids[1] = { + 0x02, +}; + static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -162,6 +181,11 @@ static hda_nid_t stac9200_pin_nids[8] = { 0x0f, 0x10, 0x11, 0x12, }; +static hda_nid_t stac925x_pin_nids[8] = { + 0x07, 0x08, 0x0a, 0x0b, + 0x0c, 0x0d, 0x10, 0x11, +}; + static hda_nid_t stac922x_pin_nids[10] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x15, 0x1b, @@ -241,6 +265,12 @@ static struct hda_verb stac9200_core_init[] = { {} }; +static struct hda_verb stac925x_core_init[] = { + /* set dac0mux for dac converter */ + { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + static struct hda_verb stac922x_core_init[] = { /* set master volume and direct control */ { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -286,6 +316,23 @@ static struct snd_kcontrol_new stac9200_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac925x_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT), + { } /* end */ +}; + /* This needs to be generated dynamically based on sequence */ static struct snd_kcontrol_new stac922x_mixer[] = { { @@ -411,6 +458,43 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { {} /* terminator */ }; +static unsigned int ref925x_pin_configs[8] = { + 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, + 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e, +}; + +static unsigned int stac925x_MA6_pin_configs[8] = { + 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, + 0x90a70320, 0x90100211, 0x400003f1, 0x9033032e, +}; + +static unsigned int stac925xM2_2_pin_configs[8] = { + 0x40c003f3, 0x424503f2, 0x041800f4, 0x02a19020, + 0x50a103F0, 0x90100210, 0x400003f1, 0x9033032e, +}; + +static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { + [STAC_REF] = ref925x_pin_configs, + [STAC_M2_2] = stac925xM2_2_pin_configs, + [STAC_MA6] = stac925x_MA6_pin_configs, +}; + +static const char *stac925x_models[STAC_925x_MODELS] = { + [STAC_REF] = "ref", + [STAC_M2_2] = "m2-2", + [STAC_MA6] = "m6", +}; + +static struct snd_pci_quirk stac925x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF), + SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF), + SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6), + SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2), + {} /* terminator */ +}; + static unsigned int ref922x_pin_configs[10] = { 0x01014010, 0x01016011, 0x01012012, 0x0221401f, 0x01813122, 0x01011014, 0x01441030, 0x01c41030, @@ -1699,6 +1783,56 @@ static int patch_stac9200(struct hda_codec *codec) return 0; } +static int patch_stac925x(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = 8; + spec->pin_nids = stac925x_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS, + stac925x_models, + stac925x_cfg_tbl); + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else if (stac925x_brd_tbl[spec->board_config] != NULL){ + spec->pin_configs = stac925x_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = stac925x_dac_nids; + spec->adc_nids = stac925x_adc_nids; + spec->mux_nids = stac925x_mux_nids; + spec->num_muxes = 1; + spec->num_dmics = 0; + + spec->init = stac925x_core_init; + spec->mixer = stac925x_mixer; + + err = stac92xx_parse_auto_config(codec, 0x8, 0x7); + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + static int patch_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -2149,6 +2283,12 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x }, { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x }, { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x }, + { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x }, + { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x }, + { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x }, + { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x }, + { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x }, + { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x }, /* The following does not take into account .id=0x83847661 when subsys = * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are * currently not fully supported. -- cgit v1.2.3-59-g8ed1b From f36090fe04986dbcd304e1f4d9224be00e57ec25 Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Mon, 8 Jan 2007 11:07:12 +0100 Subject: [ALSA] hda-codec - Add support for Samsung Q1 Ultra This adds support for the Samsung Q1 Ultra tablet pc. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_analog.c | 26 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index ce2f5faa930f..3a1bd3aefbc7 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -861,6 +861,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. 3stack 3-stack, shared surrounds laptop 2-channel only (FSC V2060, Samsung M50) laptop-eapd 2-channel with EAPD (Samsung R65, ASUS A6J) + ultra 2-channel with EAPD (Samsung Ultra tablet PC) AD1988 6stack 6-jack diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2e18a716a095..38977bce70e2 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -782,16 +782,28 @@ static struct hda_channel_mode ad1986a_modes[3] = { /* eapd initialization */ static struct hda_verb ad1986a_eapd_init_verbs[] = { - {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, + {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, {} }; +/* Ultra initialization */ +static struct hda_verb ad1986a_ultra_init[] = { + /* eapd initialization */ + { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, + /* CLFE -> Mic in */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 }, + { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, + { } /* end */ +}; + /* models */ enum { AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, AD1986A_LAPTOP_EAPD, + AD1986A_ULTRA, AD1986A_MODELS }; @@ -800,6 +812,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = { [AD1986A_3STACK] = "3stack", [AD1986A_LAPTOP] = "laptop", [AD1986A_LAPTOP_EAPD] = "laptop-eapd", + [AD1986A_ULTRA] = "ultra", }; static struct snd_pci_quirk ad1986a_cfg_tbl[] = { @@ -821,6 +834,8 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), + SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), @@ -887,6 +902,15 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; break; + case AD1986A_ULTRA: + spec->mixers[0] = ad1986a_laptop_eapd_mixers; + spec->num_init_verbs = 2; + spec->init_verbs[1] = ad1986a_ultra_init; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = ad1986a_laptop_dac_nids; + spec->multiout.dig_out_nid = 0; + break; } return 0; -- cgit v1.2.3-59-g8ed1b From 2a296cb6633a719846eaf30fcec7f392c511537d Mon Sep 17 00:00:00 2001 From: Leonard Norrgard Date: Mon, 8 Jan 2007 11:28:22 +0100 Subject: [ALSA] sound: hda: detect ALC883 on MSI K9A Platinum motherboards (MS-7280) Recognize the Realtek ALC883 chip on MSI K9A Platinum motherboards (model no. MS-7280), enabling full sound capabilities. Error messages seen before this patch: cannot find the slot for index 0 (range 0-0) hda-intel: Error creating card! HDA Intel: probe of 0000:00:14.2 failed with error -12 [akpm@osdl.org: updated to match recent ALSA table changes] Signed-off-by: Leonard Norrgard Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 468e9a09f44d..fcd9ab8dea4f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5524,6 +5524,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), -- cgit v1.2.3-59-g8ed1b From 30e80a279d864385306ed4bf905f00196d1c9656 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 9 Jan 2007 09:55:54 +0100 Subject: [ALSA] hda-codec - add ASUS W7J (0x1043, 0x1205) to quirk list - 3stack See Novell-bug#228201 . Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fcd9ab8dea4f..db261bb92162 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7589,6 +7589,7 @@ static const char *alc861_models[ALC861_MODEL_LAST] = { }; static struct snd_pci_quirk alc861_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST}, SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), -- cgit v1.2.3-59-g8ed1b From 687a47bd829e040094cbc103126d5f03d46fd2bb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Jan 2007 11:25:58 +0100 Subject: [ALSA] Fix a typo in the last patch_realtek.c change Fixed a typo in the last patch_realtek.c change. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index db261bb92162..d3d6e61abc08 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7589,7 +7589,7 @@ static const char *alc861_models[ALC861_MODEL_LAST] = { }; static struct snd_pci_quirk alc861_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST}, + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), -- cgit v1.2.3-59-g8ed1b From 9f428175a5e486b7142d3217c20481e003f3c275 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Fri, 12 Jan 2007 19:11:47 +0100 Subject: [ALSA] ac97 - Fix vt1617a build ops This patch connects the extra vt1616 controls for the vt1617a, which is necessary to control the rear speakers on e.g. a Shuttle SN25P. Signed-off-by: Daniel Jacobowitz Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index f5b4b44bbdab..f1950fa1f0ef 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -3285,6 +3285,7 @@ int patch_vt1617a(struct snd_ac97 * ac97) snd_ac97_write_cache(ac97, 0x5c, 0x20); ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; + ac97->build_ops = &patch_vt1616_ops; return 0; } -- cgit v1.2.3-59-g8ed1b From ad4d1dea62b6981a8c12df529e955424141d4b7a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Jan 2007 17:46:35 +0100 Subject: [ALSA] Fix irq handler arguments in documents Fixed the irq handler arguments in documents (removing pt_regs). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index a319905c2c72..9b4458e5313b 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -1360,8 +1360,7 @@ Interrupt Handler Case #1 lock); @@ -3106,8 +3104,7 @@ struct _snd_pcm_runtime { Interrupt Handler Case #2 lock); -- cgit v1.2.3-59-g8ed1b From 5c33dd70b51be0617a75562aa896d5ccf1840455 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 16 Jan 2007 17:49:21 +0100 Subject: [ALSA] cleanup and error reporting for sound/core/init.c Make the control flow clear with indentation, adds some comments and improves error reporting. Signed-off-by: Oliver Neukum Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/init.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index a4cc6b155ae9..db6103733742 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -114,22 +114,28 @@ struct snd_card *snd_card_new(int idx, const char *xid, if (idx < 0) { int idx2; for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) + /* idx == -1 == 0xffff means: take any free slot */ if (~snd_cards_lock & idx & 1<= snd_ecards_limit) snd_ecards_limit = idx + 1; break; } - } else if (idx < snd_ecards_limit) { - if (snd_cards_lock & (1 << idx)) - err = -ENODEV; /* invalid */ - } else if (idx < SNDRV_CARDS) - snd_ecards_limit = idx + 1; /* increase the limit */ - else - err = -ENODEV; + } else { + if (idx < snd_ecards_limit) { + if (snd_cards_lock & (1 << idx)) + err = -EBUSY; /* invalid */ + } else { + if (idx < SNDRV_CARDS) + snd_ecards_limit = idx + 1; /* increase the limit */ + else + err = -ENODEV; + } + } if (idx < 0 || err < 0) { mutex_unlock(&snd_card_mutex); - snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); + snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n", + idx, snd_ecards_limit - 1, err); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ -- cgit v1.2.3-59-g8ed1b From 7ed07a740b886930a299d438947ad322272eece1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Jan 2007 14:51:57 +0100 Subject: [ALSA] hda-intel - Don't try to probe invalid codecs Fix the max number of codecs detected by HD-intel (and compatible) controllers to 3. Some hardware reports extra bits as if connected, and the driver gets confused to probe unexisting codecs. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e6a1e37b373a..83d1ba7f33a0 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -199,7 +199,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* STATESTS int mask: SD2,SD1,SD0 */ #define STATESTS_INT_MASK 0x07 -#define AZX_MAX_CODECS 4 +#define AZX_MAX_CODECS 3 /* SD_CTL bits */ #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -- cgit v1.2.3-59-g8ed1b From f7ba7fc6173a9fb6d8a5bc02bf335cc358f21a09 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Jan 2007 18:34:47 +0100 Subject: [ALSA] emu10k1 - Fix ABI for older ld10k1 Fix ABI for older ld10k1. When no EMU10K1_PVERSION ioctl is issued, the driver accepts ioctls with the old struct size without TLV information. Also, changed the struct field to make the conversion easier from the old to the new structs. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/emu10k1.h | 21 ++++++++-- sound/pci/emu10k1/emufx.c | 100 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index adca71b20daa..975df288ce49 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1449,6 +1449,7 @@ struct snd_emu10k1 { unsigned int tos_link: 1, /* tos link detected */ rear_ac97: 1, /* rear channels are on AC'97 */ enable_ir: 1; + unsigned int support_tlv :1; /* Contains profile of card capabilities */ const struct snd_emu_chip_details *card_capabilities; unsigned int audigy; /* is Audigy? */ @@ -1901,11 +1902,20 @@ struct snd_emu10k1_fx8010_control_gpr { unsigned int value[32]; /* initial values */ unsigned int min; /* minimum range */ unsigned int max; /* maximum range */ - union { - snd_kcontrol_tlv_rw_t *c; - unsigned int *p; - } tlv; unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ + unsigned int *tlv; +}; + +/* old ABI without TLV support */ +struct snd_emu10k1_fx8010_control_old_gpr { + struct snd_ctl_elem_id id; + unsigned int vcount; + unsigned int count; + unsigned short gpr[32]; + unsigned int value[32]; + unsigned int min; + unsigned int max; + unsigned int translation; }; struct snd_emu10k1_fx8010_code { @@ -1956,6 +1966,8 @@ struct snd_emu10k1_fx8010_pcm_rec { unsigned int res2; /* reserved */ }; +#define SNDRV_EMU10K1_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1) + #define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, struct snd_emu10k1_fx8010_info) #define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, struct snd_emu10k1_fx8010_code) #define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOWR('H', 0x12, struct snd_emu10k1_fx8010_code) @@ -1964,6 +1976,7 @@ struct snd_emu10k1_fx8010_pcm_rec { #define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOWR('H', 0x22, struct snd_emu10k1_fx8010_tram) #define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, struct snd_emu10k1_fx8010_pcm_rec) #define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, struct snd_emu10k1_fx8010_pcm_rec) +#define SNDRV_EMU10K1_IOCTL_PVERSION _IOR ('H', 0x40, int) #define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) #define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) #define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index d8e8db89535f..7b173c496953 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -655,13 +655,66 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id) return NULL; } +#define MAX_TLV_SIZE 256 + +static unsigned int *copy_tlv(unsigned int __user *_tlv) +{ + unsigned int data[2]; + unsigned int *tlv; + + if (!_tlv) + return NULL; + if (copy_from_user(data, _tlv, sizeof(data))) + return NULL; + if (data[1] >= MAX_TLV_SIZE) + return NULL; + tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL); + if (!tlv) + return NULL; + memcpy(tlv, data, sizeof(data)); + if (copy_from_user(tlv + 2, _tlv + 2, data[1])) { + kfree(tlv); + return NULL; + } + return tlv; +} + +static int copy_gctl(struct snd_emu10k1 *emu, + struct snd_emu10k1_fx8010_control_gpr *gctl, + struct snd_emu10k1_fx8010_control_gpr __user *_gctl, + int idx) +{ + struct snd_emu10k1_fx8010_control_old_gpr __user *octl; + + if (emu->support_tlv) + return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl)); + octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl; + if (copy_from_user(gctl, &octl[idx], sizeof(*octl))) + return -EFAULT; + gctl->tlv = NULL; + return 0; +} + +static int copy_gctl_to_user(struct snd_emu10k1 *emu, + struct snd_emu10k1_fx8010_control_gpr __user *_gctl, + struct snd_emu10k1_fx8010_control_gpr *gctl, + int idx) +{ + struct snd_emu10k1_fx8010_control_old_gpr __user *octl; + + if (emu->support_tlv) + return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl)); + + octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl; + return copy_to_user(&octl[idx], gctl, sizeof(*octl)); +} + static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, struct snd_emu10k1_fx8010_code *icode) { unsigned int i; struct snd_ctl_elem_id __user *_id; struct snd_ctl_elem_id id; - struct snd_emu10k1_fx8010_control_gpr __user *_gctl; struct snd_emu10k1_fx8010_control_gpr *gctl; int err; @@ -676,9 +729,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, if (! gctl) return -ENOMEM; err = 0; - for (i = 0, _gctl = icode->gpr_add_controls; - i < icode->gpr_add_control_count; i++, _gctl++) { - if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { + for (i = 0; i < icode->gpr_add_control_count; i++) { + if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { err = -EFAULT; goto __error; } @@ -697,10 +749,9 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, goto __error; } } - for (i = 0, _gctl = icode->gpr_list_controls; - i < icode->gpr_list_control_count; i++, _gctl++) { + for (i = 0; i < icode->gpr_list_control_count; i++) { /* FIXME: we need to check the WRITE access */ - if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { + if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) { err = -EFAULT; goto __error; } @@ -718,13 +769,14 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl) kctl->private_value = 0; list_del(&ctl->list); kfree(ctl); + if (kctl->tlv.p) + kfree(kctl->tlv.p); } static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, struct snd_emu10k1_fx8010_code *icode) { unsigned int i, j; - struct snd_emu10k1_fx8010_control_gpr __user *_gctl; struct snd_emu10k1_fx8010_control_gpr *gctl; struct snd_emu10k1_fx8010_ctl *ctl, *nctl; struct snd_kcontrol_new knew; @@ -740,9 +792,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, goto __error; } - for (i = 0, _gctl = icode->gpr_add_controls; - i < icode->gpr_add_control_count; i++, _gctl++) { - if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { + for (i = 0; i < icode->gpr_add_control_count; i++) { + if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { err = -EFAULT; goto __error; } @@ -763,11 +814,10 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, knew.device = gctl->id.device; knew.subdevice = gctl->id.subdevice; knew.info = snd_emu10k1_gpr_ctl_info; - if (gctl->tlv.p) { - knew.tlv.p = gctl->tlv.p; + knew.tlv.p = copy_tlv(gctl->tlv); + if (knew.tlv.p) knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; - } knew.get = snd_emu10k1_gpr_ctl_get; knew.put = snd_emu10k1_gpr_ctl_put; memset(nctl, 0, sizeof(*nctl)); @@ -785,12 +835,14 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, ctl = kmalloc(sizeof(*ctl), GFP_KERNEL); if (ctl == NULL) { err = -ENOMEM; + kfree(knew.tlv.p); goto __error; } knew.private_value = (unsigned long)ctl; *ctl = *nctl; if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) { kfree(ctl); + kfree(knew.tlv.p); goto __error; } kctl->private_free = snd_emu10k1_ctl_private_free; @@ -841,7 +893,6 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, unsigned int i = 0, j; unsigned int total = 0; struct snd_emu10k1_fx8010_control_gpr *gctl; - struct snd_emu10k1_fx8010_control_gpr __user *_gctl; struct snd_emu10k1_fx8010_ctl *ctl; struct snd_ctl_elem_id *id; struct list_head *list; @@ -850,11 +901,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, if (! gctl) return -ENOMEM; - _gctl = icode->gpr_list_controls; list_for_each(list, &emu->fx8010.gpr_ctl) { ctl = emu10k1_gpr_ctl(list); total++; - if (_gctl && i < icode->gpr_list_control_count) { + if (icode->gpr_list_controls && + i < icode->gpr_list_control_count) { memset(gctl, 0, sizeof(*gctl)); id = &ctl->kcontrol->id; gctl->id.iface = id->iface; @@ -871,11 +922,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, gctl->min = ctl->min; gctl->max = ctl->max; gctl->translation = ctl->translation; - if (copy_to_user(_gctl, gctl, sizeof(*gctl))) { + if (copy_gctl_to_user(emu, icode->gpr_list_controls, + gctl, i)) { kfree(gctl); return -EFAULT; } - _gctl++; i++; } } @@ -1026,7 +1077,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl, ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->min = 0; ctl->max = 100; - ctl->tlv.p = snd_emu10k1_db_scale1; + ctl->tlv = snd_emu10k1_db_scale1; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; } @@ -1041,7 +1092,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl, ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; ctl->min = 0; ctl->max = 100; - ctl->tlv.p = snd_emu10k1_db_scale1; + ctl->tlv = snd_emu10k1_db_scale1; ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; } @@ -1566,7 +1617,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) seg = snd_enter_user(); icode->gpr_add_control_count = nctl; icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; + emu->support_tlv = 1; /* support TLV */ err = snd_emu10k1_icode_poke(emu, icode); + emu->support_tlv = 0; /* clear again */ snd_leave_user(seg); __err: @@ -2183,7 +2236,9 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) seg = snd_enter_user(); icode->gpr_add_control_count = i; icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; + emu->support_tlv = 1; /* support TLV */ err = snd_emu10k1_icode_poke(emu, icode); + emu->support_tlv = 0; /* clear again */ snd_leave_user(seg); if (err >= 0) err = snd_emu10k1_ipcm_poke(emu, ipcm); @@ -2327,6 +2382,9 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un int res; switch (cmd) { + case SNDRV_EMU10K1_IOCTL_PVERSION: + emu->support_tlv = 1; + return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp); case SNDRV_EMU10K1_IOCTL_INFO: info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) -- cgit v1.2.3-59-g8ed1b From 5bda9fa1aefbb873f2bd181e63ce0d4231883c46 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Mon, 22 Jan 2007 14:54:33 +0100 Subject: [ALSA] Documentation/sound/alsa/DocBook: typos Some typos in Documentation/sound/alsa/DocBook. Signed-off-by: Nicolas Kaiser Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl | 4 ++-- .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl index 1f3ae3e32d69..c4d2e3507af9 100644 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -36,7 +36,7 @@ Management of Cards and Devices - Card Managment + Card Management !Esound/core/init.c Device Components @@ -59,7 +59,7 @@ PCM Format Helpers !Esound/core/pcm_misc.c - PCM Memory Managment + PCM Memory Management !Esound/core/pcm_memory.c diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 9b4458e5313b..74d3a35b59bc 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -2126,7 +2126,7 @@ accessible via substream->runtime. This runtime pointer holds the various information; it holds the copy of hw_params and sw_params configurations, the buffer - pointers, mmap records, spinlocks, etc. Almost everyhing you + pointers, mmap records, spinlocks, etc. Almost everything you need for controlling the PCM can be found there. @@ -2339,7 +2339,7 @@ struct _snd_pcm_runtime { When the PCM substreams can be synchronized (typically, - synchorinized start/stop of a playback and a capture streams), + synchronized start/stop of a playback and a capture streams), you can give SNDRV_PCM_INFO_SYNC_START, too. In this case, you'll need to check the linked-list of PCM substreams in the trigger callback. This will be @@ -3244,7 +3244,7 @@ struct _snd_pcm_runtime { You can even define your own constraint rules. For example, let's suppose my_chip can manage a substream of 1 channel if and only if the format is S16_LE, otherwise it supports any format - specified in the snd_pcm_hardware stucture (or in any + specified in the snd_pcm_hardware structure (or in any other constraint_list). You can build a rule like this: @@ -3767,7 +3767,7 @@ struct _snd_pcm_runtime { Like get callback, when the control has more than one elements, - all elemehts must be evaluated in this callback, too. + all elements must be evaluated in this callback, too. @@ -5528,12 +5528,12 @@ struct _snd_pcm_runtime { #ifdef CONFIG_PM static int snd_my_suspend(struct pci_dev *pci, pm_message_t state) { - .... /* do things for suspsend */ + .... /* do things for suspend */ return 0; } static int snd_my_resume(struct pci_dev *pci) { - .... /* do things for suspsend */ + .... /* do things for suspend */ return 0; } #endif @@ -6098,7 +6098,7 @@ struct _snd_pcm_runtime { - + Acknowledgments I would like to thank Phil Kerr for his help for improvement and -- cgit v1.2.3-59-g8ed1b From 579c84a9b225d8b9d0f32818b9959ca63b4fb57d Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 23 Jan 2007 19:22:26 +0100 Subject: [ALSA] echo3g_dsp.c shouldn't include #include Despite being under linux/, linux/irq.h shouldn't be #include'd by arch independent code. Signed-off-by: Adrian Bunk Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/echoaudio/echo3g_dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index d26a1d1f3ed1..48eb7c599111 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -39,7 +39,7 @@ static int set_phantom_power(struct echoaudio *chip, char on); static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, char force); -#include +#include static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { -- cgit v1.2.3-59-g8ed1b From 32360416322ddfcd2db2f7655f606c5b86a29102 Mon Sep 17 00:00:00 2001 From: Thomas De Schampheleire Date: Wed, 24 Jan 2007 16:13:35 +0100 Subject: [ALSA] hda-codec - Missing Mic Boost on Realtek ALC882/883 This patch adds Mic Boost controls for Realtek ALC882 and ALC883 chips. Signed-off-by: Thomas De Schampheleire Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d3d6e61abc08..a29862509032 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4403,8 +4403,10 @@ static struct snd_kcontrol_new alc882_base_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), @@ -5104,8 +5106,10 @@ static struct snd_kcontrol_new alc883_base_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), @@ -5134,8 +5138,10 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), @@ -5170,8 +5176,10 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), @@ -5206,8 +5214,10 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), @@ -5241,6 +5251,7 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), @@ -5265,6 +5276,7 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), -- cgit v1.2.3-59-g8ed1b From 757e119bf52b014b3181eed97b01f87a245b8ff9 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Thu, 25 Jan 2007 13:15:05 +0100 Subject: [ALSA] Add snd-portman2x4 driver for Midiman Portman 2x4 MIDI device snd-portman2x4 driver supports Midiman Portman 2x4 parallel port MIDI device. Signed-off-by: Matthias Koenig Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 7 + sound/drivers/Kconfig | 11 + sound/drivers/Makefile | 2 + sound/drivers/portman2x4.c | 876 ++++++++++++++++++++++++ 4 files changed, 896 insertions(+) create mode 100644 sound/drivers/portman2x4.c diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 3a1bd3aefbc7..79a44f5a570a 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1411,6 +1411,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. This module supports multiple cards. + Module snd-portman2x4 + --------------------- + + Module for Midiman Portman 2x4 parallel port MIDI interface + + This module supports multiple cards. + Module snd-powermac (on ppc only) --------------------------------- diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 40ebd2f44056..83529b08d019 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -109,4 +109,15 @@ config SND_MPU401 To compile this driver as a module, choose M here: the module will be called snd-mpu401. +config SND_PORTMAN2X4 + tristate "Portman 2x4 driver" + depends on SND && PARPORT + select SND_RAWMIDI + help + Say Y here to include support for Midiman Portman 2x4 parallel + port MIDI device. + + To compile this driver as a module, choose M here: the module + will be called snd-portman2x4. + endmenu diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index c9bad6d67e73..04112642611a 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -6,6 +6,7 @@ snd-dummy-objs := dummy.o snd-mtpav-objs := mtpav.o snd-mts64-objs := mts64.o +snd-portman2x4-objs := portman2x4.o snd-serial-u16550-objs := serial-u16550.o snd-virmidi-objs := virmidi.o @@ -15,5 +16,6 @@ obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTS64) += snd-mts64.o +obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c new file mode 100644 index 000000000000..6c48772aaefd --- /dev/null +++ b/sound/drivers/portman2x4.c @@ -0,0 +1,876 @@ +/* + * Driver for Midiman Portman2x4 parallel port midi interface + * + * Copyright (c) by Levent Guendogdu + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ChangeLog + * Jan 24 2007 Matthias Koenig + * - cleanup and rewrite + * Sep 30 2004 Tobias Gehrig + * - source code cleanup + * Sep 03 2004 Tobias Gehrig + * - fixed compilation problem with alsa 1.0.6a (removed MODULE_CLASSES, + * MODULE_PARM_SYNTAX and changed MODULE_DEVICES to + * MODULE_SUPPORTED_DEVICE) + * Mar 24 2004 Tobias Gehrig + * - added 2.6 kernel support + * Mar 18 2004 Tobias Gehrig + * - added parport_unregister_driver to the startup routine if the driver fails to detect a portman + * - added support for all 4 output ports in portman_putmidi + * Mar 17 2004 Tobias Gehrig + * - added checks for opened input device in interrupt handler + * Feb 20 2004 Tobias Gehrig + * - ported from alsa 0.5 to 1.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CARD_NAME "Portman 2x4" +#define DRIVER_NAME "portman" +#define PLATFORM_DRIVER "snd_portman2x4" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +static struct platform_device *platform_devices[SNDRV_CARDS]; +static int device_count; + +module_param_array(index, int, NULL, S_IRUGO); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +module_param_array(id, charp, NULL, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, S_IRUGO); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); + +MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig"); +MODULE_DESCRIPTION("Midiman Portman2x4"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Midiman,Portman2x4}}"); + +/********************************************************************* + * Chip specific + *********************************************************************/ +#define PORTMAN_NUM_INPUT_PORTS 2 +#define PORTMAN_NUM_OUTPUT_PORTS 4 + +struct portman { + spinlock_t reg_lock; + struct snd_card *card; + struct snd_rawmidi *rmidi; + struct pardevice *pardev; + int pardev_claimed; + + int open_count; + int mode[PORTMAN_NUM_INPUT_PORTS]; + struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS]; +}; + +static int portman_free(struct portman *pm) +{ + kfree(pm); + return 0; +} + +static int __devinit portman_create(struct snd_card *card, + struct pardevice *pardev, + struct portman **rchip) +{ + struct portman *pm; + + *rchip = NULL; + + pm = kzalloc(sizeof(struct portman), GFP_KERNEL); + if (pm == NULL) + return -ENOMEM; + + /* Init chip specific data */ + spin_lock_init(&pm->reg_lock); + pm->card = card; + pm->pardev = pardev; + + *rchip = pm; + + return 0; +} + +/********************************************************************* + * HW related constants + *********************************************************************/ + +/* Standard PC parallel port status register equates. */ +#define PP_STAT_BSY 0x80 /* Busy status. Inverted. */ +#define PP_STAT_ACK 0x40 /* Acknowledge. Non-Inverted. */ +#define PP_STAT_POUT 0x20 /* Paper Out. Non-Inverted. */ +#define PP_STAT_SEL 0x10 /* Select. Non-Inverted. */ +#define PP_STAT_ERR 0x08 /* Error. Non-Inverted. */ + +/* Standard PC parallel port command register equates. */ +#define PP_CMD_IEN 0x10 /* IRQ Enable. Non-Inverted. */ +#define PP_CMD_SELI 0x08 /* Select Input. Inverted. */ +#define PP_CMD_INIT 0x04 /* Init Printer. Non-Inverted. */ +#define PP_CMD_FEED 0x02 /* Auto Feed. Inverted. */ +#define PP_CMD_STB 0x01 /* Strobe. Inverted. */ + +/* Parallel Port Command Register as implemented by PCP2x4. */ +#define INT_EN PP_CMD_IEN /* Interrupt enable. */ +#define STROBE PP_CMD_STB /* Command strobe. */ + +/* The parallel port command register field (b1..b3) selects the + * various "registers" within the PC/P 2x4. These are the internal + * address of these "registers" that must be written to the parallel + * port command register. + */ +#define RXDATA0 (0 << 1) /* PCP RxData channel 0. */ +#define RXDATA1 (1 << 1) /* PCP RxData channel 1. */ +#define GEN_CTL (2 << 1) /* PCP General Control Register. */ +#define SYNC_CTL (3 << 1) /* PCP Sync Control Register. */ +#define TXDATA0 (4 << 1) /* PCP TxData channel 0. */ +#define TXDATA1 (5 << 1) /* PCP TxData channel 1. */ +#define TXDATA2 (6 << 1) /* PCP TxData channel 2. */ +#define TXDATA3 (7 << 1) /* PCP TxData channel 3. */ + +/* Parallel Port Status Register as implemented by PCP2x4. */ +#define ESTB PP_STAT_POUT /* Echoed strobe. */ +#define INT_REQ PP_STAT_ACK /* Input data int request. */ +#define BUSY PP_STAT_ERR /* Interface Busy. */ + +/* Parallel Port Status Register BUSY and SELECT lines are multiplexed + * between several functions. Depending on which 2x4 "register" is + * currently selected (b1..b3), the BUSY and SELECT lines are + * assigned as follows: + * + * SELECT LINE: A3 A2 A1 + * -------- + */ +#define RXAVAIL PP_STAT_SEL /* Rx Available, channel 0. 0 0 0 */ +// RXAVAIL1 PP_STAT_SEL /* Rx Available, channel 1. 0 0 1 */ +#define SYNC_STAT PP_STAT_SEL /* Reserved - Sync Status. 0 1 0 */ +// /* Reserved. 0 1 1 */ +#define TXEMPTY PP_STAT_SEL /* Tx Empty, channel 0. 1 0 0 */ +// TXEMPTY1 PP_STAT_SEL /* Tx Empty, channel 1. 1 0 1 */ +// TXEMPTY2 PP_STAT_SEL /* Tx Empty, channel 2. 1 1 0 */ +// TXEMPTY3 PP_STAT_SEL /* Tx Empty, channel 3. 1 1 1 */ + +/* BUSY LINE: A3 A2 A1 + * -------- + */ +#define RXDATA PP_STAT_BSY /* Rx Input Data, channel 0. 0 0 0 */ +// RXDATA1 PP_STAT_BSY /* Rx Input Data, channel 1. 0 0 1 */ +#define SYNC_DATA PP_STAT_BSY /* Reserved - Sync Data. 0 1 0 */ + /* Reserved. 0 1 1 */ +#define DATA_ECHO PP_STAT_BSY /* Parallel Port Data Echo. 1 0 0 */ +#define A0_ECHO PP_STAT_BSY /* Address 0 Echo. 1 0 1 */ +#define A1_ECHO PP_STAT_BSY /* Address 1 Echo. 1 1 0 */ +#define A2_ECHO PP_STAT_BSY /* Address 2 Echo. 1 1 1 */ + +#define PORTMAN2X4_MODE_INPUT_TRIGGERED 0x01 + +/********************************************************************* + * Hardware specific functions + *********************************************************************/ +static inline void portman_write_command(struct portman *pm, u8 value) +{ + parport_write_control(pm->pardev->port, value); +} + +static inline u8 portman_read_command(struct portman *pm) +{ + return parport_read_control(pm->pardev->port); +} + +static inline u8 portman_read_status(struct portman *pm) +{ + return parport_read_status(pm->pardev->port); +} + +static inline u8 portman_read_data(struct portman *pm) +{ + return parport_read_data(pm->pardev->port); +} + +static inline void portman_write_data(struct portman *pm, u8 value) +{ + parport_write_data(pm->pardev->port, value); +} + +static void portman_write_midi(struct portman *pm, + int port, u8 mididata) +{ + int command = ((port + 4) << 1); + + /* Get entering data byte and port number in BL and BH respectively. + * Set up Tx Channel address field for use with PP Cmd Register. + * Store address field in BH register. + * Inputs: AH = Output port number (0..3). + * AL = Data byte. + * command = TXDATA0 | INT_EN; + * Align port num with address field (b1...b3), + * set address for TXDatax, Strobe=0 + */ + command |= INT_EN; + + /* Disable interrupts so that the process is not interrupted, then + * write the address associated with the current Tx channel to the + * PP Command Reg. Do not set the Strobe signal yet. + */ + + do { + portman_write_command(pm, command); + + /* While the address lines settle, write parallel output data to + * PP Data Reg. This has no effect until Strobe signal is asserted. + */ + + portman_write_data(pm, mididata); + + /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP + * Status Register), then go write data. Else go back and wait. + */ + } while ((portman_read_status(pm) & TXEMPTY) != TXEMPTY); + + /* TxEmpty is set. Maintain PC/P destination address and assert + * Strobe through the PP Command Reg. This will Strobe data into + * the PC/P transmitter and set the PC/P BUSY signal. + */ + + portman_write_command(pm, command | STROBE); + + /* Wait for strobe line to settle and echo back through hardware. + * Once it has echoed back, assume that the address and data lines + * have settled! + */ + + while ((portman_read_status(pm) & ESTB) == 0) + cpu_relax(); + + /* Release strobe and immediately re-allow interrupts. */ + portman_write_command(pm, command); + + while ((portman_read_status(pm) & ESTB) == ESTB) + cpu_relax(); + + /* PC/P BUSY is now set. We must wait until BUSY resets itself. + * We'll reenable ints while we're waiting. + */ + + while ((portman_read_status(pm) & BUSY) == BUSY) + cpu_relax(); + + /* Data sent. */ +} + + +/* + * Read MIDI byte from port + * Attempt to read input byte from specified hardware input port (0..). + * Return -1 if no data + */ +static int portman_read_midi(struct portman *pm, int port) +{ + unsigned char midi_data = 0; + unsigned char cmdout; /* Saved address+IE bit. */ + + /* Make sure clocking edge is down before starting... */ + portman_write_data(pm, 0); /* Make sure edge is down. */ + + /* Set destination address to PCP. */ + cmdout = (port << 1) | INT_EN; /* Address + IE + No Strobe. */ + portman_write_command(pm, cmdout); + + while ((portman_read_status(pm) & ESTB) == ESTB) + cpu_relax(); /* Wait for strobe echo. */ + + /* After the address lines settle, check multiplexed RxAvail signal. + * If data is available, read it. + */ + if ((portman_read_status(pm) & RXAVAIL) == 0) + return -1; /* No data. */ + + /* Set the Strobe signal to enable the Rx clocking circuitry. */ + portman_write_command(pm, cmdout | STROBE); /* Write address+IE+Strobe. */ + + while ((portman_read_status(pm) & ESTB) == 0) + cpu_relax(); /* Wait for strobe echo. */ + + /* The first data bit (msb) is already sitting on the input line. */ + midi_data = (portman_read_status(pm) & 128); + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 6. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 1) & 64; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 5. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 2) & 32; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 4. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 3) & 16; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 3. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 4) & 8; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 2. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 5) & 4; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 1. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 6) & 2; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + + /* Data bit 0. */ + portman_write_data(pm, 0); /* Cause falling edge while data settles. */ + midi_data |= (portman_read_status(pm) >> 7) & 1; + portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ + portman_write_data(pm, 0); /* Return data clock low. */ + + + /* De-assert Strobe and return data. */ + portman_write_command(pm, cmdout); /* Output saved address+IE. */ + + /* Wait for strobe echo. */ + while ((portman_read_status(pm) & ESTB) == ESTB) + cpu_relax(); + + return (midi_data & 255); /* Shift back and return value. */ +} + +/* + * Checks if any input data on the given channel is available + * Checks RxAvail + */ +static int portman_data_avail(struct portman *pm, int channel) +{ + int command = INT_EN; + switch (channel) { + case 0: + command |= RXDATA0; + break; + case 1: + command |= RXDATA1; + break; + } + /* Write hardware (assumme STROBE=0) */ + portman_write_command(pm, command); + /* Check multiplexed RxAvail signal */ + if ((portman_read_status(pm) & RXAVAIL) == RXAVAIL) + return 1; /* Data available */ + + /* No Data available */ + return 0; +} + + +/* + * Flushes any input + */ +static void portman_flush_input(struct portman *pm, unsigned char port) +{ + /* Local variable for counting things */ + unsigned int i = 0; + unsigned char command = 0; + + switch (port) { + case 0: + command = RXDATA0; + break; + case 1: + command = RXDATA1; + break; + default: + snd_printk(KERN_WARNING + "portman_flush_input() Won't flush port %i\n", + port); + return; + } + + /* Set address for specified channel in port and allow to settle. */ + portman_write_command(pm, command); + + /* Assert the Strobe and wait for echo back. */ + portman_write_command(pm, command | STROBE); + + /* Wait for ESTB */ + while ((portman_read_status(pm) & ESTB) == 0) + cpu_relax(); + + /* Output clock cycles to the Rx circuitry. */ + portman_write_data(pm, 0); + + /* Flush 250 bits... */ + for (i = 0; i < 250; i++) { + portman_write_data(pm, 1); + portman_write_data(pm, 0); + } + + /* Deassert the Strobe signal of the port and wait for it to settle. */ + portman_write_command(pm, command | INT_EN); + + /* Wait for settling */ + while ((portman_read_status(pm) & ESTB) == ESTB) + cpu_relax(); +} + +static int portman_probe(struct parport *p) +{ + /* Initialize the parallel port data register. Will set Rx clocks + * low in case we happen to be addressing the Rx ports at this time. + */ + /* 1 */ + parport_write_data(p, 0); + + /* Initialize the parallel port command register, thus initializing + * hardware handshake lines to midi box: + * + * Strobe = 0 + * Interrupt Enable = 0 + */ + /* 2 */ + parport_write_control(p, 0); + + /* Check if Portman PC/P 2x4 is out there. */ + /* 3 */ + parport_write_control(p, RXDATA0); /* Write Strobe=0 to command reg. */ + + /* Check for ESTB to be clear */ + /* 4 */ + if ((parport_read_status(p) & ESTB) == ESTB) + return 1; /* CODE 1 - Strobe Failure. */ + + /* Set for RXDATA0 where no damage will be done. */ + /* 5 */ + parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */ + + /* 6 */ + if ((parport_read_status(p) & ESTB) != ESTB) + return 1; /* CODE 1 - Strobe Failure. */ + + /* 7 */ + parport_write_control(p, 0); /* Reset Strobe=0. */ + + /* Check if Tx circuitry is functioning properly. If initialized + * unit TxEmpty is false, send out char and see if if goes true. + */ + /* 8 */ + parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */ + + /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP + * Status Register), then go write data. Else go back and wait. + */ + /* 9 */ + if ((parport_read_status(p) & TXEMPTY) == 0) + return 2; + + /* Return OK status. */ + return 0; +} + +static int portman_device_init(struct portman *pm) +{ + portman_flush_input(pm, 0); + portman_flush_input(pm, 1); + + return 0; +} + +/********************************************************************* + * Rawmidi + *********************************************************************/ +static int snd_portman_midi_open(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static int snd_portman_midi_close(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static void snd_portman_midi_input_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct portman *pm = substream->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&pm->reg_lock, flags); + if (up) + pm->mode[substream->number] |= PORTMAN2X4_MODE_INPUT_TRIGGERED; + else + pm->mode[substream->number] &= ~PORTMAN2X4_MODE_INPUT_TRIGGERED; + spin_unlock_irqrestore(&pm->reg_lock, flags); +} + +static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct portman *pm = substream->rmidi->private_data; + unsigned long flags; + unsigned char byte; + + spin_lock_irqsave(&pm->reg_lock, flags); + if (up) { + while ((snd_rawmidi_transmit(substream, &byte, 1) == 1)) + portman_write_midi(pm, substream->number, byte); + } + spin_unlock_irqrestore(&pm->reg_lock, flags); +} + +static struct snd_rawmidi_ops snd_portman_midi_output = { + .open = snd_portman_midi_open, + .close = snd_portman_midi_close, + .trigger = snd_portman_midi_output_trigger, +}; + +static struct snd_rawmidi_ops snd_portman_midi_input = { + .open = snd_portman_midi_open, + .close = snd_portman_midi_close, + .trigger = snd_portman_midi_input_trigger, +}; + +/* Create and initialize the rawmidi component */ +static int __devinit snd_portman_rawmidi_create(struct snd_card *card) +{ + struct portman *pm = card->private_data; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *substream; + int err; + + err = snd_rawmidi_new(card, CARD_NAME, 0, + PORTMAN_NUM_OUTPUT_PORTS, + PORTMAN_NUM_INPUT_PORTS, + &rmidi); + if (err < 0) + return err; + + rmidi->private_data = pm; + strcpy(rmidi->name, CARD_NAME); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + pm->rmidi = rmidi; + + /* register rawmidi ops */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_portman_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_portman_midi_input); + + /* name substreams */ + /* output */ + list_for_each_entry(substream, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, + list) { + sprintf(substream->name, + "Portman2x4 %d", substream->number+1); + } + /* input */ + list_for_each_entry(substream, + &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams, + list) { + pm->midi_input[substream->number] = substream; + sprintf(substream->name, + "Portman2x4 %d", substream->number+1); + } + + return err; +} + +/********************************************************************* + * parport stuff + *********************************************************************/ +static void snd_portman_interrupt(int irq, void *userdata) +{ + unsigned char midivalue = 0; + struct portman *pm = ((struct snd_card*)userdata)->private_data; + + spin_lock(&pm->reg_lock); + + /* While any input data is waiting */ + while ((portman_read_status(pm) & INT_REQ) == INT_REQ) { + /* If data available on channel 0, + read it and stuff it into the queue. */ + if (portman_data_avail(pm, 0)) { + /* Read Midi */ + midivalue = portman_read_midi(pm, 0); + /* put midi into queue... */ + if (pm->mode[0] & PORTMAN2X4_MODE_INPUT_TRIGGERED) + snd_rawmidi_receive(pm->midi_input[0], + &midivalue, 1); + + } + /* If data available on channel 1, + read it and stuff it into the queue. */ + if (portman_data_avail(pm, 1)) { + /* Read Midi */ + midivalue = portman_read_midi(pm, 1); + /* put midi into queue... */ + if (pm->mode[1] & PORTMAN2X4_MODE_INPUT_TRIGGERED) + snd_rawmidi_receive(pm->midi_input[1], + &midivalue, 1); + } + + } + + spin_unlock(&pm->reg_lock); +} + +static int __devinit snd_portman_probe_port(struct parport *p) +{ + struct pardevice *pardev; + int res; + + pardev = parport_register_device(p, DRIVER_NAME, + NULL, NULL, NULL, + 0, NULL); + if (!pardev) + return -EIO; + + if (parport_claim(pardev)) { + parport_unregister_device(pardev); + return -EIO; + } + + res = portman_probe(p); + + parport_release(pardev); + parport_unregister_device(pardev); + + return res; +} + +static void __devinit snd_portman_attach(struct parport *p) +{ + struct platform_device *device; + + device = platform_device_alloc(PLATFORM_DRIVER, device_count); + if (!device) + return; + + /* Temporary assignment to forward the parport */ + platform_set_drvdata(device, p); + + if (platform_device_register(device) < 0) { + platform_device_put(device); + return; + } + + /* Since we dont get the return value of probe + * We need to check if device probing succeeded or not */ + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + return; + } + + /* register device in global table */ + platform_devices[device_count] = device; + device_count++; +} + +static void snd_portman_detach(struct parport *p) +{ + /* nothing to do here */ +} + +static struct parport_driver portman_parport_driver = { + .name = "portman2x4", + .attach = snd_portman_attach, + .detach = snd_portman_detach +}; + +/********************************************************************* + * platform stuff + *********************************************************************/ +static void snd_portman_card_private_free(struct snd_card *card) +{ + struct portman *pm = card->private_data; + struct pardevice *pardev = pm->pardev; + + if (pardev) { + if (pm->pardev_claimed) + parport_release(pardev); + parport_unregister_device(pardev); + } + + portman_free(pm); +} + +static int __devinit snd_portman_probe(struct platform_device *pdev) +{ + struct pardevice *pardev; + struct parport *p; + int dev = pdev->id; + struct snd_card *card = NULL; + struct portman *pm = NULL; + int err; + + p = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) + return -ENOENT; + + if ((err = snd_portman_probe_port(p)) < 0) + return err; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) { + snd_printd("Cannot create card\n"); + return -ENOMEM; + } + strcpy(card->driver, DRIVER_NAME); + strcpy(card->shortname, CARD_NAME); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, p->base, p->irq); + + pardev = parport_register_device(p, /* port */ + DRIVER_NAME, /* name */ + NULL, /* preempt */ + NULL, /* wakeup */ + snd_portman_interrupt, /* ISR */ + PARPORT_DEV_EXCL, /* flags */ + (void *)card); /* private */ + if (pardev == NULL) { + snd_printd("Cannot register pardevice\n"); + err = -EIO; + goto __err; + } + + if ((err = portman_create(card, pardev, &pm)) < 0) { + snd_printd("Cannot create main component\n"); + parport_unregister_device(pardev); + goto __err; + } + card->private_data = pm; + card->private_free = snd_portman_card_private_free; + + if ((err = snd_portman_rawmidi_create(card)) < 0) { + snd_printd("Creating Rawmidi component failed\n"); + goto __err; + } + + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto __err; + } + pm->pardev_claimed = 1; + + /* init device */ + if ((err = portman_device_init(pm)) < 0) + goto __err; + + platform_set_drvdata(pdev, card); + + /* At this point card will be usable */ + if ((err = snd_card_register(card)) < 0) { + snd_printd("Cannot register card\n"); + goto __err; + } + + snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base); + return 0; + +__err: + snd_card_free(card); + return err; +} + +static int snd_portman_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + if (card) + snd_card_free(card); + + return 0; +} + + +static struct platform_driver snd_portman_driver = { + .probe = snd_portman_probe, + .remove = snd_portman_remove, + .driver = { + .name = PLATFORM_DRIVER + } +}; + +/********************************************************************* + * module init stuff + *********************************************************************/ +static void snd_portman_unregister_all(void) +{ + int i; + + for (i = 0; i < SNDRV_CARDS; ++i) { + if (platform_devices[i]) { + platform_device_unregister(platform_devices[i]); + platform_devices[i] = NULL; + } + } + platform_driver_unregister(&snd_portman_driver); + parport_unregister_driver(&portman_parport_driver); +} + +static int __init snd_portman_module_init(void) +{ + int err; + + if ((err = platform_driver_register(&snd_portman_driver)) < 0) + return err; + + if (parport_register_driver(&portman_parport_driver) != 0) { + platform_driver_unregister(&snd_portman_driver); + return -EIO; + } + + if (device_count == 0) { + snd_portman_unregister_all(); + return -ENODEV; + } + + return 0; +} + +static void __exit snd_portman_module_exit(void) +{ + snd_portman_unregister_all(); +} + +module_init(snd_portman_module_init); +module_exit(snd_portman_module_exit); -- cgit v1.2.3-59-g8ed1b From cd7509a43c3047a6339484e5009c2db7ee4c7a51 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 26 Jan 2007 18:33:17 +0100 Subject: [ALSA] hda-codec - Add HP BPC-D7000 support Add HP BPC-D7000 support. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 153 +++++++++++++++++++++++- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 79a44f5a570a..7d5a1d00259d 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -806,6 +806,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ALC262 fujitsu Fujitsu Laptop hp-bpc HP xw4400/6400/8400/9400 laptops + hp-bpc-d7000 HP BPC D7000 benq Benq ED8 hippo Hippo (ATI) with jack detection, Sony UX-90s hippo_1 Hippo (Benq) with jack detection diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a29862509032..d01895515e0d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -87,6 +87,8 @@ enum { ALC262_HIPPO_1, ALC262_FUJITSU, ALC262_HP_BPC, + ALC262_HP_BPC_D7000_WL, + ALC262_HP_BPC_D7000_WF, ALC262_BENQ_ED8, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ @@ -5919,6 +5921,30 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { + HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -6448,6 +6474,100 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { { } }; +static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for front + * panel mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)}, + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ + /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + + { } +}; + /* pcm configuration: identiacal with ALC880 */ #define alc262_pcm_analog_playback alc880_pcm_analog_playback #define alc262_pcm_analog_capture alc880_pcm_analog_capture @@ -6511,6 +6631,7 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_HIPPO_1] = "hippo_1", [ALC262_FUJITSU] = "fujitsu", [ALC262_HP_BPC] = "hp-bpc", + [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", [ALC262_BENQ_ED8] = "benq", [ALC262_AUTO] = "auto", }; @@ -6519,9 +6640,16 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x2801, "HP q954", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), @@ -6586,6 +6714,27 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_HP_capture_source, }, + [ALC262_HP_BPC_D7000_WF] = { + .mixers = { alc262_HP_BPC_WildWest_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, + [ALC262_HP_BPC_D7000_WL] = { + .mixers = { alc262_HP_BPC_WildWest_mixer, + alc262_HP_BPC_WildWest_option_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, [ALC262_BENQ_ED8] = { .mixers = { alc262_base_mixer }, .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, @@ -6623,7 +6772,7 @@ static int patch_alc262(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, alc262_models, alc262_cfg_tbl); - + if (board_config < 0) { printk(KERN_INFO "hda_codec: Unknown model for ALC262, " "trying auto-probe from BIOS...\n"); -- cgit v1.2.3-59-g8ed1b From 377a4f7ea35d7d6cc05faea7030522d93435dc11 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Mon, 29 Jan 2007 14:45:53 +0100 Subject: [ALSA] sound/isa/gus/gus_main.c: Use abs() instead of x < 0 ? -x : x. Signed-off-by: Peter Eriksen Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/isa/gus/gus_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index b680fddf0d74..8ced5e81b9a7 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -294,10 +294,10 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches) gus->mix_cntrl_reg |= 4; /* enable MIC */ } dma1 = gus->gf1.dma1; - dma1 = dma1 < 0 ? -dma1 : dma1; + dma1 = abs(dma1); dma1 = dmas[dma1 & 7]; dma2 = gus->gf1.dma2; - dma2 = dma2 < 0 ? -dma2 : dma2; + dma2 = abs(dma2); dma2 = dmas[dma2 & 7]; dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); @@ -306,7 +306,7 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches) return -EINVAL; } irq = gus->gf1.irq; - irq = irq < 0 ? -irq : irq; + irq = abs(irq); irq = irqs[irq & 0x0f]; if (irq == 0) { snd_printk(KERN_ERR "Error! IRQ isn't defined.\n"); -- cgit v1.2.3-59-g8ed1b From bcb4d788f573805c74ac4f39a622b30955b2f916 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Mon, 29 Jan 2007 14:46:18 +0100 Subject: [ALSA] Remove useless reference to obsolete KERNELD Remove the final useless reference to the obsolete KERNELD feature. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/timer.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 4e79f9ce1a85..3e0638351069 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -35,9 +35,6 @@ #include #include #include -#ifdef CONFIG_KERNELD -#include -#endif #if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE) #define DEFAULT_TIMER_LIMIT 3 -- cgit v1.2.3-59-g8ed1b From 189bc171434e84797f586130fca8eb4df3746e3f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jan 2007 15:25:40 +0100 Subject: [ALSA] ice1712 - Reorganize existing eeprom data Reorganize EEPROM data (in C99 style). Signed-of-by: Philipp Matthias Hahn Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ice1712/aureon.c | 112 +++++++++++++++------------------------- sound/pci/ice1712/juli.c | 26 +++++----- sound/pci/ice1712/phase.c | 52 +++++++++---------- sound/pci/ice1712/pontis.c | 26 +++++----- sound/pci/ice1712/prodigy192.c | 26 +++++----- sound/pci/ice1712/vt1720_mobo.c | 53 +++++++++---------- 6 files changed, 133 insertions(+), 162 deletions(-) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index a085618d33f9..041d2f07572b 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -2110,84 +2110,54 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) */ static unsigned char aureon51_eeprom[] __devinitdata = { - 0x0a, /* SYSCONF: clock 512, spdif-in/ADC, 3DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x5f, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; static unsigned char aureon71_eeprom[] __devinitdata = { - 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ -}; - -static unsigned char prodigy71_eeprom[] __devinitdata = { - 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x5f, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; +#define prodigy71_eeprom aureon71_eeprom static unsigned char prodigy71lt_eeprom[] __devinitdata = { - 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ -}; - -static unsigned char prodigy71xt_eeprom[] __devinitdata = { - 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x5f, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; +#define prodigy71xt_eeprom prodigy71lt_eeprom /* entry point */ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = { diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 5176b41ea9d3..a7e779b7142a 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -207,19 +207,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice) */ static unsigned char juli_eeprom[] __devinitdata = { - 0x20, /* SYSCONF: clock 512, mpu401, 1xADC, 1xDACs */ - 0x80, /* ACLINK: I2S */ - 0xf8, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0x9f, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x7f, /* GPIO_DIR2 */ - 0x9f, /* GPIO_MASK */ - 0xff, /* GPIO_MASK1 */ - 0x7f, /* GPIO_MASK2 */ - 0x16, /* GPIO_STATE: internal clock, multiple 1x, 48kHz */ - 0x80, /* GPIO_STATE1: mute */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0x9f, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x7f, + [ICE_EEP2_GPIO_MASK] = 0x9f, + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x7f, + [ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */ + [ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */ + [ICE_EEP2_GPIO_STATE2] = 0x00, }; /* entry point */ diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index e08d73f4ff85..c7f6615d60d2 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -153,35 +153,35 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice) } static unsigned char phase22_eeprom[] __devinitdata = { - 0x00, /* SYSCONF: 1xADC, 1xDACs */ - 0x80, /* ACLINK: I2S */ - 0xf8, /* I2S: vol, 96k, 24bit*/ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xFF, /* GPIO_DIR */ - 0xFF, /* GPIO_DIR1 */ - 0xFF, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE: */ - 0x00, /* GPIO_STATE1: */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0xff, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; static unsigned char phase28_eeprom[] __devinitdata = { - 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x5f, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x5f, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; /* diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 6c74c2d2e7f3..b135c1401dc7 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -827,19 +827,19 @@ static int __devinit pontis_init(struct snd_ice1712 *ice) */ static unsigned char pontis_eeprom[] __devinitdata = { - 0x08, /* SYSCONF: clock 256, mpu401, spdif-in/ADC, 1DAC */ - 0x80, /* ACLINK: I2S */ - 0xf8, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0x07, /* GPIO_DIR */ - 0x00, /* GPIO_DIR1 */ - 0x00, /* GPIO_DIR2 (ignored) */ - 0x0f, /* GPIO_MASK (4-7 reserved for CS8416) */ - 0xff, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 (ignored) */ - 0x06, /* GPIO_STATE (0-low, 1-high, 2-high) */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 (ignored) */ + [ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0x07, + [ICE_EEP2_GPIO_DIR1] = 0x00, + [ICE_EEP2_GPIO_DIR2] = 0x00, /* ignored */ + [ICE_EEP2_GPIO_MASK] = 0x0f, /* 4-7 reserved for CS8416 */ + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x00, /* ignored */ + [ICE_EEP2_GPIO_STATE] = 0x06, /* 0-low, 1-high, 2-high */ + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, /* ignored */ }; /* entry point */ diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 41b2605daa3a..b9b63179dbec 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -507,19 +507,19 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) */ static unsigned char prodigy71_eeprom[] __devinitdata = { - 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */ - 0x80, /* ACLINK: I2S */ - 0xf8, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDIF: out-en, out-int, spdif-in */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0xbf, /* GPIO_DIR2 */ - 0x00, /* GPIO_MASK */ - 0x00, /* GPIO_MASK1 */ - 0x00, /* GPIO_MASK2 */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0xbf, + [ICE_EEP2_GPIO_MASK] = 0x00, + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0x00, + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 7ca263c13091..239524158fe7 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -30,6 +30,7 @@ #include #include "ice1712.h" +#include "envy24ht.h" #include "vt1720_mobo.h" @@ -56,35 +57,35 @@ static int __devinit k8x800_add_controls(struct snd_ice1712 *ice) /* EEPROM image */ static unsigned char k8x800_eeprom[] __devinitdata = { - 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */ - 0x02, /* ACLINK: ACLINK, packed */ - 0x00, /* I2S: - */ - 0x00, /* SPDIF: - */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x00, /* - */ - 0xff, /* GPIO_MASK */ - 0xff, /* GPIO_MASK1 */ - 0x00, /* - */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* - */ + [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */ + [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */ + [ICE_EEP2_I2S] = 0x00, /* - */ + [ICE_EEP2_SPDIF] = 0x00, /* - */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x00, /* - */ + [ICE_EEP2_GPIO_MASK] = 0xff, + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x00, /* - */ + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, /* - */ }; static unsigned char sn25p_eeprom[] __devinitdata = { - 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */ - 0x02, /* ACLINK: ACLINK, packed */ - 0x00, /* I2S: - */ - 0x41, /* SPDIF: - */ - 0xff, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x00, /* - */ - 0xff, /* GPIO_MASK */ - 0xff, /* GPIO_MASK1 */ - 0x00, /* - */ - 0x00, /* GPIO_STATE */ - 0x00, /* GPIO_STATE1 */ - 0x00, /* - */ + [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */ + [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */ + [ICE_EEP2_I2S] = 0x00, /* - */ + [ICE_EEP2_SPDIF] = 0x41, /* - */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x00, /* - */ + [ICE_EEP2_GPIO_MASK] = 0xff, + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x00, /* - */ + [ICE_EEP2_GPIO_STATE] = 0x00, + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, /* - */ }; -- cgit v1.2.3-59-g8ed1b From 32b47da03541f97e40f1af5488ef88250459f388 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jan 2007 15:26:36 +0100 Subject: [ALSA] Add 'const' to files in pci/ice1712/ Mark a lot of data as 'const' Signed-of-by: Philipp Matthias Hahn Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ice1712/amp.c | 4 +-- sound/pci/ice1712/amp.h | 2 +- sound/pci/ice1712/aureon.c | 42 +++++++++++------------ sound/pci/ice1712/aureon.h | 2 +- sound/pci/ice1712/delta.c | 34 +++++++++---------- sound/pci/ice1712/delta.h | 2 +- sound/pci/ice1712/ews.c | 24 ++++++------- sound/pci/ice1712/ews.h | 2 +- sound/pci/ice1712/hoontech.c | 7 ++-- sound/pci/ice1712/hoontech.h | 2 +- sound/pci/ice1712/ice1712.c | 72 +++++++++++++++++++-------------------- sound/pci/ice1712/ice1712.h | 4 +-- sound/pci/ice1712/ice1724.c | 74 ++++++++++++++++++++--------------------- sound/pci/ice1712/juli.c | 6 ++-- sound/pci/ice1712/juli.h | 2 +- sound/pci/ice1712/phase.c | 20 +++++------ sound/pci/ice1712/phase.h | 2 +- sound/pci/ice1712/pontis.c | 14 ++++---- sound/pci/ice1712/pontis.h | 2 +- sound/pci/ice1712/prodigy192.c | 10 +++--- sound/pci/ice1712/prodigy192.h | 2 +- sound/pci/ice1712/revo.c | 22 ++++++------ sound/pci/ice1712/revo.h | 2 +- sound/pci/ice1712/vt1720_mobo.c | 6 ++-- sound/pci/ice1712/vt1720_mobo.h | 2 +- sound/pci/ice1712/wtm.c | 2 +- 26 files changed, 180 insertions(+), 183 deletions(-) diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 59c4078ad331..6e22d326df32 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -42,7 +42,7 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val) static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice) { - static unsigned short wm_inits[] = { + static const unsigned short wm_inits[] = { WM_ATTEN_L, 0x0000, /* 0 db */ WM_ATTEN_R, 0x0000, /* 0 db */ WM_DAC_CTRL, 0x0008, /* 24bit I2S */ @@ -75,7 +75,7 @@ static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice) /* entry point */ -struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_AV710, .name = "Chaintech AV-710", diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h index a0fc89b48122..7b667bad0c6b 100644 --- a/sound/pci/ice1712/amp.h +++ b/sound/pci/ice1712/amp.h @@ -42,7 +42,7 @@ #define WM_DAC_CTRL 0x02 #define WM_INT_CTRL 0x03 -extern struct snd_ice1712_card_info snd_vt1724_amp_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_amp_cards[]; #endif /* __SOUND_AMP_H */ diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 041d2f07572b..625a9a32b7c7 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -294,7 +294,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r static int aureon_ac97_init (struct snd_ice1712 *ice) { int i; - static unsigned short ac97_defaults[] = { + static const unsigned short ac97_defaults[] = { 0x00, 0x9640, 0x02, 0x8000, 0x04, 0x8000, @@ -674,7 +674,7 @@ static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0); * Logarithmic volume values for WM8770 * Computed as 20 * Log10(255 / x) */ -static unsigned char wm_vol[256] = { +static const unsigned char wm_vol[256] = { 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, @@ -1067,14 +1067,14 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val */ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { + static const char * const texts[] = { "CD", //AIN1 "Aux", //AIN2 "Line", //AIN3 "Mic", //AIN4 "AC97" //AIN5 }; - static char *universe_texts[] = { + static const char * const universe_texts[] = { "Aux1", //AIN1 "CD", //AIN2 "Phono", //AIN3 @@ -1140,11 +1140,11 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static char *aureon_texts[] = { + static const char * const aureon_texts[] = { "CD", //RXP0 "Optical" //RXP1 }; - static char *prodigy_texts[] = { + static const char * const prodigy_texts[] = { "CD", "Coax" }; @@ -1368,7 +1368,7 @@ static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v */ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "128x", "64x" }; + static const char * const texts[2] = { "128x", "64x" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1411,7 +1411,7 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl * mixers */ -static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = { +static const struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -1526,7 +1526,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = { } }; -static struct snd_kcontrol_new wm_controls[] __devinitdata = { +static const struct snd_kcontrol_new wm_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", @@ -1592,7 +1592,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = { } }; -static struct snd_kcontrol_new ac97_controls[] __devinitdata = { +static const struct snd_kcontrol_new ac97_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "AC97 Playback Switch", @@ -1697,7 +1697,7 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = { } }; -static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = { +static const struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "AC97 Playback Switch", @@ -1829,8 +1829,7 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = { }; - -static struct snd_kcontrol_new cs8415_controls[] __devinitdata = { +static const struct snd_kcontrol_new cs8415_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), @@ -1875,7 +1874,6 @@ static struct snd_kcontrol_new cs8415_controls[] __devinitdata = { } }; - static int __devinit aureon_add_controls(struct snd_ice1712 *ice) { unsigned int i, counts; @@ -1943,7 +1941,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice) */ static int __devinit aureon_init(struct snd_ice1712 *ice) { - static unsigned short wm_inits_aureon[] = { + static const unsigned short wm_inits_aureon[] = { /* These come first to reduce init pop noise */ 0x1b, 0x044, /* ADC Mux (AC'97 source) */ 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ @@ -1979,7 +1977,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) 0x1a, 0x000, /* -12dB ADC/R */ (unsigned short)-1 }; - static unsigned short wm_inits_prodigy[] = { + static const unsigned short wm_inits_prodigy[] = { /* These come first to reduce init pop noise */ 0x1b, 0x000, /* ADC Mux */ @@ -2021,7 +2019,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) (unsigned short)-1 }; - static unsigned short cs_inits[] = { + static const unsigned short cs_inits[] = { 0x0441, /* RUN */ 0x0180, /* no mute, OMCK output on RMCK pin */ 0x0201, /* S/PDIF source on RXP1 */ @@ -2029,7 +2027,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) (unsigned short)-1 }; unsigned int tmp; - unsigned short *p; + const unsigned short *p; int err, i; if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { @@ -2109,7 +2107,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) * hence the driver needs to sets up it properly. */ -static unsigned char aureon51_eeprom[] __devinitdata = { +static const unsigned char aureon51_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ @@ -2125,7 +2123,7 @@ static unsigned char aureon51_eeprom[] __devinitdata = { [ICE_EEP2_GPIO_STATE2] = 0x00, }; -static unsigned char aureon71_eeprom[] __devinitdata = { +static const unsigned char aureon71_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ @@ -2142,7 +2140,7 @@ static unsigned char aureon71_eeprom[] __devinitdata = { }; #define prodigy71_eeprom aureon71_eeprom -static unsigned char prodigy71lt_eeprom[] __devinitdata = { +static const unsigned char prodigy71lt_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ @@ -2160,7 +2158,7 @@ static unsigned char prodigy71lt_eeprom[] __devinitdata = { #define prodigy71xt_eeprom prodigy71lt_eeprom /* entry point */ -struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_AUREON51_SKY, .name = "Terratec Aureon 5.1-Sky", diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h index c253b8e2c789..79e58e88ed47 100644 --- a/sound/pci/ice1712/aureon.h +++ b/sound/pci/ice1712/aureon.h @@ -38,7 +38,7 @@ #define VT1724_SUBDEVICE_PRODIGY71LT 0x32315441 /* PRODIGY 7.1 LT */ #define VT1724_SUBDEVICE_PRODIGY71XT 0x36315441 /* PRODIGY 7.1 XT*/ -extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_aureon_cards[]; /* GPIO bits */ #define AUREON_CS8415_CS (1 << 22) diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index af659800c9b0..3eeb36c6e985 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -416,7 +416,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco return 0; } -static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata = { .access = (SNDRV_CTL_ELEM_ACCESS_READ), .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -429,7 +429,7 @@ static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devini * initialize the chips on M-Audio cards */ -static struct snd_akm4xxx akm_audiophile __devinitdata = { +static const struct snd_akm4xxx akm_audiophile __devinitdata = { .type = SND_AK4528, .num_adcs = 2, .num_dacs = 2, @@ -438,7 +438,7 @@ static struct snd_akm4xxx akm_audiophile __devinitdata = { } }; -static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = { .caddr = 2, .cif = 0, .data_mask = ICE1712_DELTA_AP_DOUT, @@ -450,7 +450,7 @@ static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_delta410 __devinitdata = { +static const struct snd_akm4xxx akm_delta410 __devinitdata = { .type = SND_AK4529, .num_adcs = 2, .num_dacs = 8, @@ -459,7 +459,7 @@ static struct snd_akm4xxx akm_delta410 __devinitdata = { } }; -static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_delta410_priv __devinitdata = { .caddr = 0, .cif = 0, .data_mask = ICE1712_DELTA_AP_DOUT, @@ -471,7 +471,7 @@ static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_delta1010lt __devinitdata = { +static const struct snd_akm4xxx akm_delta1010lt __devinitdata = { .type = SND_AK4524, .num_adcs = 8, .num_dacs = 8, @@ -481,7 +481,7 @@ static struct snd_akm4xxx akm_delta1010lt __devinitdata = { } }; -static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = { .caddr = 2, .cif = 0, /* the default level of the CIF pin from AK4524 */ .data_mask = ICE1712_DELTA_1010LT_DOUT, @@ -493,7 +493,7 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_delta44 __devinitdata = { +static const struct snd_akm4xxx akm_delta44 __devinitdata = { .type = SND_AK4524, .num_adcs = 4, .num_dacs = 4, @@ -503,7 +503,7 @@ static struct snd_akm4xxx akm_delta44 __devinitdata = { } }; -static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_delta44_priv __devinitdata = { .caddr = 2, .cif = 0, /* the default level of the CIF pin from AK4524 */ .data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA, @@ -515,7 +515,7 @@ static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_vx442 __devinitdata = { +static const struct snd_akm4xxx akm_vx442 __devinitdata = { .type = SND_AK4524, .num_adcs = 4, .num_dacs = 4, @@ -525,7 +525,7 @@ static struct snd_akm4xxx akm_vx442 __devinitdata = { } }; -static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_vx442_priv __devinitdata = { .caddr = 2, .cif = 0, .data_mask = ICE1712_VX442_DOUT, @@ -650,15 +650,15 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) * additional controls for M-Audio cards */ -static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata = ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); -static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata = ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 0, 0); -static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata = ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); -static struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata = ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); -static struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata = ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); @@ -735,7 +735,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) /* entry point */ -struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { { .subvendor = ICE1712_SUBDEVICE_DELTA1010, .name = "M Audio Delta 1010", diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index 746ebde94522..e65d669af639 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -46,7 +46,7 @@ #define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100 /* entry point */ -extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; +extern const struct snd_ice1712_card_info snd_ice1712_delta_cards[]; /* diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index b135389fec6c..9b7ff302c072 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -332,7 +332,7 @@ static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate) /* */ -static struct snd_akm4xxx akm_ews88mt __devinitdata = { +static const struct snd_akm4xxx akm_ews88mt __devinitdata = { .num_adcs = 8, .num_dacs = 8, .type = SND_AK4524, @@ -342,7 +342,7 @@ static struct snd_akm4xxx akm_ews88mt __devinitdata = { } }; -static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = { .caddr = 2, .cif = 1, /* CIF high */ .data_mask = ICE1712_EWS88_SERIAL_DATA, @@ -354,7 +354,7 @@ static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_ewx2496 __devinitdata = { +static const struct snd_akm4xxx akm_ewx2496 __devinitdata = { .num_adcs = 2, .num_dacs = 2, .type = SND_AK4524, @@ -363,7 +363,7 @@ static struct snd_akm4xxx akm_ewx2496 __devinitdata = { } }; -static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = { .caddr = 2, .cif = 1, /* CIF high */ .data_mask = ICE1712_EWS88_SERIAL_DATA, @@ -375,7 +375,7 @@ static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_6fire __devinitdata = { +static const struct snd_akm4xxx akm_6fire __devinitdata = { .num_adcs = 6, .num_dacs = 6, .type = SND_AK4524, @@ -384,7 +384,7 @@ static struct snd_akm4xxx akm_6fire __devinitdata = { } }; -static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_6fire_priv __devinitdata = { .caddr = 2, .cif = 1, /* CIF high */ .data_mask = ICE1712_6FIRE_SERIAL_DATA, @@ -578,7 +578,7 @@ static int snd_ice1712_ewx_io_sense_put(struct snd_kcontrol *kcontrol, struct sn return val != nval; } -static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Sensitivity Switch", @@ -678,7 +678,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st return ndata != data; } -static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Sensitivity Switch", .info = snd_ice1712_ewx_io_sense_info, @@ -687,7 +687,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = { .count = 8, }; -static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Output Sensitivity Switch", .info = snd_ice1712_ewx_io_sense_info, @@ -769,7 +769,7 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct .private_value = xshift | (xinvert << 8),\ } -static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = { EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */ EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0), EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0), @@ -909,7 +909,7 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str .private_value = xshift | (xinvert << 8),\ } -static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Input Select", @@ -989,7 +989,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice) /* entry point */ -struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { { .subvendor = ICE1712_SUBDEVICE_EWX2496, .name = "TerraTec EWX24/96", diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h index a12a0b053558..df449b4741f6 100644 --- a/sound/pci/ice1712/ews.h +++ b/sound/pci/ice1712/ews.h @@ -40,7 +40,7 @@ #define ICE1712_SUBDEVICE_PHASE88 0x3b155111 /* entry point */ -extern struct snd_ice1712_card_info snd_ice1712_ews_cards[]; +extern const struct snd_ice1712_card_info snd_ice1712_ews_cards[]; /* TerraTec EWX 24/96 configuration definitions */ diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index 3f27d04e7d3c..df97313aaf83 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -239,7 +239,7 @@ static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip) static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice) { /* Hoontech STDSP24 with modified hardware */ - static struct snd_akm4xxx akm_stdsp24_mv __devinitdata = { + static const struct snd_akm4xxx akm_stdsp24_mv __devinitdata = { .num_adcs = 2, .num_dacs = 2, .type = SND_AK4524, @@ -248,7 +248,7 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice) } }; - static struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = { + static const struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = { .caddr = 2, .cif = 1, /* CIF high */ .data_mask = ICE1712_STDSP24_SERIAL_DATA, @@ -298,7 +298,7 @@ static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice) /* entry point */ -struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { { .subvendor = ICE1712_SUBDEVICE_STDSP24, .name = "Hoontech SoundTrack Audio DSP24", @@ -325,4 +325,3 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { }, { } /* terminator */ }; - diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h index 1ee538b20fbf..b62d6e4f6c71 100644 --- a/sound/pci/ice1712/hoontech.h +++ b/sound/pci/ice1712/hoontech.h @@ -35,7 +35,7 @@ #define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */ #define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */ -extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[]; +extern const struct snd_ice1712_card_info snd_ice1712_hoontech_cards[]; /* Hoontech SoundTrack Audio DSP 24 GPIO definitions */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 8ba31cfb9045..b8baadba810c 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -107,7 +107,7 @@ module_param_array(dxr_enable, int, NULL, 0444); MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE."); -static struct pci_device_id snd_ice1712_ids[] = { +static const struct pci_device_id snd_ice1712_ids[] = { { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */ { 0, } }; @@ -287,7 +287,7 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru return val != nval; } -static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Mixer To AC97", .info = snd_ice1712_digmix_route_ac97_info, @@ -719,7 +719,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s return bytes_to_frames(substream->runtime, ptr); } -static struct snd_pcm_hardware snd_ice1712_playback = +static const struct snd_pcm_hardware snd_ice1712_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -739,7 +739,7 @@ static struct snd_pcm_hardware snd_ice1712_playback = .fifo_size = 0, }; -static struct snd_pcm_hardware snd_ice1712_playback_ds = +static const struct snd_pcm_hardware snd_ice1712_playback_ds = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -759,7 +759,7 @@ static struct snd_pcm_hardware snd_ice1712_playback_ds = .fifo_size = 0, }; -static struct snd_pcm_hardware snd_ice1712_capture = +static const struct snd_pcm_hardware snd_ice1712_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -1133,7 +1133,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea return bytes_to_frames(substream->runtime, ptr); } -static struct snd_pcm_hardware snd_ice1712_playback_pro = +static const struct snd_pcm_hardware snd_ice1712_playback_pro = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -1153,7 +1153,7 @@ static struct snd_pcm_hardware snd_ice1712_playback_pro = .fifo_size = 0, }; -static struct snd_pcm_hardware snd_ice1712_capture_pro = +static const struct snd_pcm_hardware snd_ice1712_capture_pro = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -1380,7 +1380,7 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0); -static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Playback Switch", @@ -1404,7 +1404,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata }, }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Multi Capture Switch", .info = snd_ice1712_pro_mixer_switch_info, @@ -1413,7 +1413,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit .private_value = 10, }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,SWITCH), .info = snd_ice1712_pro_mixer_switch_info, @@ -1423,7 +1423,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd .count = 2, }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -1435,7 +1435,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit .tlv = { .p = db_scale_playback } }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,VOLUME), .info = snd_ice1712_pro_mixer_volume_info, @@ -1627,7 +1627,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "ICE1712 EEPROM", .access = SNDRV_CTL_ELEM_ACCESS_READ, @@ -1663,7 +1663,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), @@ -1714,7 +1714,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1723,7 +1723,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata = .get = snd_ice1712_spdif_maskc_get, }; -static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1750,7 +1750,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata = +static const struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata = { .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE), @@ -1811,7 +1811,7 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { + static const char * const texts[] = { "8000", /* 0: 6 */ "9600", /* 1: 3 */ "11025", /* 2: 10 */ @@ -1840,7 +1840,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static unsigned char xlate[16] = { + static const unsigned char xlate[16] = { 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10 }; unsigned char val; @@ -1864,7 +1864,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static unsigned int xrate[13] = { + static const unsigned int xrate[13] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; @@ -1891,7 +1891,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Internal Clock", .info = snd_ice1712_pro_internal_clock_info, @@ -1902,7 +1902,7 @@ static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = { static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { + static const char * const texts[] = { "8000", /* 0: 6 */ "9600", /* 1: 3 */ "11025", /* 2: 10 */ @@ -1931,7 +1931,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont struct snd_ctl_elem_value *ucontrol) { int val; - static unsigned int xrate[13] = { + static const unsigned int xrate[13] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; @@ -1948,7 +1948,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - static unsigned int xrate[13] = { + static const unsigned int xrate[13] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; @@ -1962,7 +1962,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont return change; } -static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Internal Clock Default", .info = snd_ice1712_pro_internal_clock_default_info, @@ -2001,7 +2001,7 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Rate Locking", .info = snd_ice1712_pro_rate_locking_info, @@ -2040,7 +2040,7 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Rate Reset", .info = snd_ice1712_pro_rate_reset_info, @@ -2054,7 +2054,7 @@ static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = { static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { + static const char * const texts[] = { "PCM Out", /* 0 */ "H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */ "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */ @@ -2207,7 +2207,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Playback Route", .info = snd_ice1712_pro_route_info, @@ -2215,7 +2215,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata .put = snd_ice1712_pro_route_analog_put, }; -static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", .info = snd_ice1712_pro_route_info, @@ -2257,7 +2257,7 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Volume Rate", .info = snd_ice1712_pro_volume_rate_info, @@ -2290,7 +2290,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = { +static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, @@ -2305,7 +2305,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = { /* * list of available boards */ -static struct snd_ice1712_card_info *card_tables[] __devinitdata = { +static const struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_ice1712_hoontech_cards, snd_ice1712_delta_cards, snd_ice1712_ews_cards, @@ -2329,7 +2329,7 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice, { int dev = 0xa0; /* EEPROM device address */ unsigned int i, size; - struct snd_ice1712_card_info **tbl, *c; + const struct snd_ice1712_card_info **tbl, *c; if (! modelname || ! *modelname) { ice->eeprom.subvendor = 0; @@ -2658,7 +2658,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card, * */ -static struct snd_ice1712_card_info no_matched __devinitdata; +static const struct snd_ice1712_card_info no_matched __devinitdata; static int __devinit snd_ice1712_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) @@ -2667,7 +2667,7 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci, struct snd_card *card; struct snd_ice1712 *ice; int pcm_dev = 0, err; - struct snd_ice1712_card_info **tbl, *c; + const struct snd_ice1712_card_info **tbl, *c; if (dev >= SNDRV_CARDS) return -ENODEV; diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 064542bf3af8..c3d9feaaf57d 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -514,8 +514,8 @@ struct snd_ice1712_card_info { unsigned int mpu401_2_info_flags; const char *mpu401_1_name; const char *mpu401_2_name; - unsigned int eeprom_size; - unsigned char *eeprom_data; + const unsigned int eeprom_size; + const unsigned char *eeprom_data; }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 4566c0f789aa..1127ebdf5fec 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -87,7 +87,7 @@ MODULE_PARM_DESC(model, "Use the given board model."); /* Both VT1720 and VT1724 have the same PCI IDs */ -static struct pci_device_id snd_vt1724_ids[] = { +static const struct pci_device_id snd_vt1724_ids[] = { { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, } }; @@ -342,7 +342,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) what = 0; snd_pcm_group_for_each(pos, substream) { - struct vt1724_pcm_reg *reg; + const struct vt1724_pcm_reg *reg; s = snd_pcm_group_substream_entry(pos); reg = s->runtime->private_data; what |= reg->start; @@ -606,7 +606,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); - struct vt1724_pcm_reg *reg = substream->runtime->private_data; + const struct vt1724_pcm_reg *reg = substream->runtime->private_data; spin_lock_irq(&ice->reg_lock); outl(substream->runtime->dma_addr, ice->profi_port + reg->addr); @@ -621,7 +621,7 @@ static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream) static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); - struct vt1724_pcm_reg *reg = substream->runtime->private_data; + const struct vt1724_pcm_reg *reg = substream->runtime->private_data; size_t ptr; if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start)) @@ -647,21 +647,21 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr #endif } -static struct vt1724_pcm_reg vt1724_playback_pro_reg = { +static const struct vt1724_pcm_reg vt1724_playback_pro_reg = { .addr = VT1724_MT_PLAYBACK_ADDR, .size = VT1724_MT_PLAYBACK_SIZE, .count = VT1724_MT_PLAYBACK_COUNT, .start = VT1724_PDMA0_START, }; -static struct vt1724_pcm_reg vt1724_capture_pro_reg = { +static const struct vt1724_pcm_reg vt1724_capture_pro_reg = { .addr = VT1724_MT_CAPTURE_ADDR, .size = VT1724_MT_CAPTURE_SIZE, .count = VT1724_MT_CAPTURE_COUNT, .start = VT1724_RDMA0_START, }; -static struct snd_pcm_hardware snd_vt1724_playback_pro = +static const struct snd_pcm_hardware snd_vt1724_playback_pro = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -680,7 +680,7 @@ static struct snd_pcm_hardware snd_vt1724_playback_pro = .periods_max = 1024, }; -static struct snd_pcm_hardware snd_vt1724_spdif = +static const struct snd_pcm_hardware snd_vt1724_spdif = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -702,7 +702,7 @@ static struct snd_pcm_hardware snd_vt1724_spdif = .periods_max = 1024, }; -static struct snd_pcm_hardware snd_vt1724_2ch_stereo = +static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -774,7 +774,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); int chs; - runtime->private_data = &vt1724_playback_pro_reg; + runtime->private_data = (void *)&vt1724_playback_pro_reg; ice->playback_pro_substream = substream; runtime->hw = snd_vt1724_playback_pro; snd_pcm_set_sync(substream); @@ -803,7 +803,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - runtime->private_data = &vt1724_capture_pro_reg; + runtime->private_data = (void *)&vt1724_capture_pro_reg; ice->capture_pro_substream = substream; runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); @@ -889,14 +889,14 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device) * SPDIF PCM */ -static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { +static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = { .addr = VT1724_MT_PDMA4_ADDR, .size = VT1724_MT_PDMA4_SIZE, .count = VT1724_MT_PDMA4_COUNT, .start = VT1724_PDMA4_START, }; -static struct vt1724_pcm_reg vt1724_capture_spdif_reg = { +static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = { .addr = VT1724_MT_RDMA1_ADDR, .size = VT1724_MT_RDMA1_SIZE, .count = VT1724_MT_RDMA1_COUNT, @@ -954,7 +954,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - runtime->private_data = &vt1724_playback_spdif_reg; + runtime->private_data = (void *)&vt1724_playback_spdif_reg; ice->playback_con_substream = substream; if (ice->force_pdma4) { runtime->hw = snd_vt1724_2ch_stereo; @@ -986,7 +986,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - runtime->private_data = &vt1724_capture_spdif_reg; + runtime->private_data = (void *)&vt1724_capture_spdif_reg; ice->capture_con_substream = substream; if (ice->force_rdma1) { runtime->hw = snd_vt1724_2ch_stereo; @@ -1091,7 +1091,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device) * independent surround PCMs */ -static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { +static const struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { { .addr = VT1724_MT_PDMA1_ADDR, .size = VT1724_MT_PDMA1_SIZE, @@ -1137,7 +1137,7 @@ static int snd_vt1724_playback_indep_open(struct snd_pcm_substream *substream) return -EBUSY; /* FIXME: should handle blocking mode properly */ } mutex_unlock(&ice->open_mutex); - runtime->private_data = &vt1724_playback_dma_regs[substream->number]; + runtime->private_data = (void *)&vt1724_playback_dma_regs[substream->number]; ice->playback_con_substream_ds[substream->number] = substream; runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); @@ -1318,7 +1318,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "ICE1724 EEPROM", .access = SNDRV_CTL_ELEM_ACCESS_READ, @@ -1431,7 +1431,7 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol, return (val != old); } -static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata = +static const struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), @@ -1463,7 +1463,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata = +static const struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1472,7 +1472,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata = .get = snd_vt1724_spdif_maskc_get, }; -static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata = +static const struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1517,7 +1517,7 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol, return old != val; } -static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata = +static const struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* FIXME: the following conflict with IEC958 Playback Route */ @@ -1585,7 +1585,7 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts_1724[] = { + static const char * const texts_1724[] = { "8000", /* 0: 6 */ "9600", /* 1: 3 */ "11025", /* 2: 10 */ @@ -1603,7 +1603,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, "192000", /* 14: 14 */ "IEC958 Input", /* 15: -- */ }; - static char *texts_1720[] = { + static const char * const texts_1720[] = { "8000", /* 0: 6 */ "9600", /* 1: 3 */ "11025", /* 2: 10 */ @@ -1636,7 +1636,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static unsigned char xlate[16] = { + static const unsigned char xlate[16] = { 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10 }; unsigned char val; @@ -1695,7 +1695,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Internal Clock", .info = snd_vt1724_pro_internal_clock_info, @@ -1734,7 +1734,7 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Rate Locking", .info = snd_vt1724_pro_rate_locking_info, @@ -1773,7 +1773,7 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Rate Reset", .info = snd_vt1724_pro_rate_reset_info, @@ -1817,7 +1817,7 @@ static int get_route_val(struct snd_ice1712 *ice, int shift) { unsigned long val; unsigned char eitem; - static unsigned char xlate[8] = { + static const unsigned char xlate[8] = { 0, 255, 1, 2, 255, 255, 3, 4, }; @@ -1836,7 +1836,7 @@ static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift) { unsigned int old_val, nval; int change; - static unsigned char xroute[8] = { + static const unsigned char xroute[8] = { 0, /* PCM */ 2, /* PSDIN0 Left */ 3, /* PSDIN0 Right */ @@ -1892,7 +1892,7 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol, digital_route_shift(idx)); } -static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Playback Route", .info = snd_vt1724_pro_route_info, @@ -1900,7 +1900,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = .put = snd_vt1724_pro_route_analog_put, }; -static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", .info = snd_vt1724_pro_route_info, @@ -1936,7 +1936,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = { +static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, @@ -1948,9 +1948,9 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = { * */ -static struct snd_ice1712_card_info no_matched __devinitdata; +static const struct snd_ice1712_card_info no_matched __devinitdata; -static struct snd_ice1712_card_info *card_tables[] __devinitdata = { +static const struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_revo_cards, snd_vt1724_amp_cards, snd_vt1724_aureon_cards, @@ -2009,7 +2009,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice, { const int dev = 0xa0; /* EEPROM device address */ unsigned int i, size; - struct snd_ice1712_card_info **tbl, *c; + const struct snd_ice1712_card_info **tbl, *c; if (! modelname || ! *modelname) { ice->eeprom.subvendor = 0; @@ -2308,7 +2308,7 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, struct snd_card *card; struct snd_ice1712 *ice; int pcm_dev = 0, err; - struct snd_ice1712_card_info **tbl, *c; + const struct snd_ice1712_card_info **tbl, *c; if (dev >= SNDRV_CARDS) return -ENODEV; diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index a7e779b7142a..4854eaf63a8a 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -125,7 +125,7 @@ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) snd_akm4xxx_reset(ak, 0); } -static struct snd_akm4xxx akm_juli_dac __devinitdata = { +static const struct snd_akm4xxx akm_juli_dac __devinitdata = { .type = SND_AK4358, .num_dacs = 2, .ops = { @@ -206,7 +206,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice) * hence the driver needs to sets up it properly. */ -static unsigned char juli_eeprom[] __devinitdata = { +static const unsigned char juli_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ @@ -223,7 +223,7 @@ static unsigned char juli_eeprom[] __devinitdata = { }; /* entry point */ -struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_JULI, .name = "ESI Juli@", diff --git a/sound/pci/ice1712/juli.h b/sound/pci/ice1712/juli.h index d9f8534fd92e..1b9294f8bce3 100644 --- a/sound/pci/ice1712/juli.h +++ b/sound/pci/ice1712/juli.h @@ -5,6 +5,6 @@ #define VT1724_SUBDEVICE_JULI 0x31305345 /* Juli@ */ -extern struct snd_ice1712_card_info snd_vt1724_juli_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_juli_cards[]; #endif /* __SOUND_JULI_H */ diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index c7f6615d60d2..2d97ac8a07d3 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -71,7 +71,7 @@ * Logarithmic volume values for WM8770 * Computed as 20 * Log10(255 / x) */ -static unsigned char wm_vol[256] = { +static const unsigned char wm_vol[256] = { 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, @@ -89,13 +89,13 @@ static unsigned char wm_vol[256] = { #define WM_VOL_MAX (sizeof(wm_vol) - 1) #define WM_VOL_MUTE 0x8000 -static struct snd_akm4xxx akm_phase22 __devinitdata = { +static const struct snd_akm4xxx akm_phase22 __devinitdata = { .type = SND_AK4524, .num_dacs = 2, .num_adcs = 2, }; -static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_phase22_priv __devinitdata = { .caddr = 2, .cif = 1, .data_mask = 1 << 4, @@ -152,7 +152,7 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice) return 0; } -static unsigned char phase22_eeprom[] __devinitdata = { +static const unsigned char phase22_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x00, /* 1xADC, 1xDACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit */ @@ -168,7 +168,7 @@ static unsigned char phase22_eeprom[] __devinitdata = { [ICE_EEP2_GPIO_STATE2] = 0x00, }; -static unsigned char phase28_eeprom[] __devinitdata = { +static const unsigned char phase28_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */ @@ -343,7 +343,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ static int __devinit phase28_init(struct snd_ice1712 *ice) { - static unsigned short wm_inits_phase28[] = { + static const unsigned short wm_inits_phase28[] = { /* These come first to reduce init pop noise */ 0x1b, 0x044, /* ADC Mux (AC'97 source) */ 0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ @@ -382,7 +382,7 @@ static int __devinit phase28_init(struct snd_ice1712 *ice) unsigned int tmp; struct snd_akm4xxx *ak; - unsigned short *p; + const unsigned short *p; int i; ice->num_total_dacs = 8; @@ -700,7 +700,7 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ct static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); -static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { +static const struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -815,7 +815,7 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { } }; -static struct snd_kcontrol_new wm_controls[] __devinitdata = { +static const struct snd_kcontrol_new wm_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", @@ -870,7 +870,7 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice) return 0; } -struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_PHASE22, .name = "Terratec PHASE 22", diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h index 13e841b55488..ad379a99bf92 100644 --- a/sound/pci/ice1712/phase.h +++ b/sound/pci/ice1712/phase.h @@ -31,7 +31,7 @@ #define VT1724_SUBDEVICE_PHASE28 0x3b154911 /* entry point */ -extern struct snd_ice1712_card_info snd_vt1724_phase_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_phase_cards[]; /* PHASE28 GPIO bits */ #define PHASE28_SPI_MISO (1 << 21) diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index b135c1401dc7..4c35ddecb8e6 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -434,7 +434,7 @@ static unsigned int spi_read(struct snd_ice1712 *ice, unsigned int dev, unsigned */ static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { + static const char * const texts[] = { "Coax", /* RXP0 */ "Optical", /* RXP1 */ "CD", /* RXP2 */ @@ -571,7 +571,7 @@ static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1); * mixers */ -static struct snd_kcontrol_new pontis_controls[] __devinitdata = { +static const struct snd_kcontrol_new pontis_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -741,7 +741,7 @@ static int __devinit pontis_add_controls(struct snd_ice1712 *ice) */ static int __devinit pontis_init(struct snd_ice1712 *ice) { - static unsigned short wm_inits[] = { + static const unsigned short wm_inits[] = { /* These come first to reduce init pop noise */ WM_ADC_MUX, 0x00c0, /* ADC mute */ WM_DAC_MUTE, 0x0001, /* DAC softmute */ @@ -750,7 +750,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice) WM_POWERDOWN, 0x0008, /* All power-up except HP */ WM_RESET, 0x0000, /* reset */ }; - static unsigned short wm_inits2[] = { + static const unsigned short wm_inits2[] = { WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */ WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */ WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */ @@ -776,7 +776,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice) WM_DAC_MUTE, 0x0000, /* DAC unmute */ WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */ }; - static unsigned char cs_inits[] = { + static const unsigned char cs_inits[] = { 0x04, 0x80, /* RUN, RXP0 */ 0x05, 0x05, /* slave, 24bit */ 0x01, 0x00, @@ -826,7 +826,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice) * hence the driver needs to sets up it properly. */ -static unsigned char pontis_eeprom[] __devinitdata = { +static const unsigned char pontis_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ @@ -843,7 +843,7 @@ static unsigned char pontis_eeprom[] __devinitdata = { }; /* entry point */ -struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = { { .subvendor = VT1720_SUBDEVICE_PONTIS_MS300, .name = "Pontis MS300", diff --git a/sound/pci/ice1712/pontis.h b/sound/pci/ice1712/pontis.h index d0d1378b935c..1a418255c19e 100644 --- a/sound/pci/ice1712/pontis.h +++ b/sound/pci/ice1712/pontis.h @@ -28,6 +28,6 @@ #define VT1720_SUBDEVICE_PONTIS_MS300 0x00020002 /* a dummy id for MS300 */ -extern struct snd_ice1712_card_info snd_vt1720_pontis_cards[]; +extern const struct snd_ice1712_card_info snd_vt1720_pontis_cards[]; #endif /* __SOUND_PONTIS_H */ diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index b9b63179dbec..d551e71c67ca 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -364,7 +364,7 @@ static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); * mixers */ -static struct snd_kcontrol_new stac_controls[] __devinitdata = { +static const struct snd_kcontrol_new stac_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -475,7 +475,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) */ static int __devinit prodigy192_init(struct snd_ice1712 *ice) { - static unsigned short stac_inits_prodigy[] = { + static const unsigned short stac_inits_prodigy[] = { STAC946X_RESET, 0, /* STAC946X_MASTER_VOLUME, 0, STAC946X_LF_VOLUME, 0, @@ -486,7 +486,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) STAC946X_LFE_VOLUME, 0,*/ (unsigned short)-1 }; - unsigned short *p; + const unsigned short *p; /* prodigy 192 */ ice->num_total_dacs = 6; @@ -506,7 +506,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) * hence the driver needs to sets up it properly. */ -static unsigned char prodigy71_eeprom[] __devinitdata = { +static const unsigned char prodigy71_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC, 4DACs */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ @@ -524,7 +524,7 @@ static unsigned char prodigy71_eeprom[] __devinitdata = { /* entry point */ -struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_PRODIGY192VE, .name = "Audiotrak Prodigy 192", diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h index 94c824e24e06..2fa2e62b9e04 100644 --- a/sound/pci/ice1712/prodigy192.h +++ b/sound/pci/ice1712/prodigy192.h @@ -6,6 +6,6 @@ #define VT1724_SUBDEVICE_PRODIGY192VE 0x34495345 /* PRODIGY 192 VE */ -extern struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[]; #endif /* __SOUND_PRODIGY192_H */ diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 0e578aa38af2..7d3bccbf0313 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -228,7 +228,7 @@ static struct snd_akm4xxx akm_revo_front __devinitdata = { .dac_info = revo71_front, }; -static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { .caddr = 1, .cif = 0, .data_mask = VT1724_REVO_CDOUT, @@ -240,7 +240,7 @@ static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_revo_surround __devinitdata = { +static const struct snd_akm4xxx akm_revo_surround __devinitdata = { .type = SND_AK4355, .idx_offset = 1, .num_dacs = 6, @@ -250,7 +250,7 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = { .dac_info = revo71_surround, }; -static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { .caddr = 3, .cif = 0, .data_mask = VT1724_REVO_CDOUT, @@ -262,7 +262,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_revo51 __devinitdata = { +static const struct snd_akm4xxx akm_revo51 __devinitdata = { .type = SND_AK4358, .num_dacs = 6, .ops = { @@ -271,7 +271,7 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = { .dac_info = revo51_dac, }; -static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { .caddr = 2, .cif = 0, .data_mask = VT1724_REVO_CDOUT, @@ -283,13 +283,13 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { .mask_flags = 0, }; -static struct snd_akm4xxx akm_revo51_adc __devinitdata = { +static const struct snd_akm4xxx akm_revo51_adc __devinitdata = { .type = SND_AK5365, .num_adcs = 2, .adc_info = revo51_adc, }; -static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = { .caddr = 2, .cif = 0, .data_mask = VT1724_REVO_CDOUT, @@ -333,7 +333,7 @@ static struct snd_akm4xxx akm_ap192 __devinitdata = { .dac_info = ap192_dac, }; -static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { +static const struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { .caddr = 2, .cif = 0, .data_mask = VT1724_REVO_CDOUT, @@ -456,7 +456,7 @@ static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr) static int ap192_ak4114_init(struct snd_ice1712 *ice) { - static unsigned char ak4114_init_vals[] = { + static const unsigned char ak4114_init_vals[] = { AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1, AK4114_DIF_I24I2S, AK4114_TX1E, @@ -464,7 +464,7 @@ static int ap192_ak4114_init(struct snd_ice1712 *ice) 0, 0 }; - static unsigned char ak4114_init_txcsb[] = { + static const unsigned char ak4114_init_txcsb[] = { 0x41, 0x02, 0x2c, 0x00, 0x00 }; struct ak4114 *ak; @@ -582,7 +582,7 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) } /* entry point */ -struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = { { .subvendor = VT1724_SUBDEVICE_REVOLUTION71, .name = "M Audio Revolution-7.1", diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h index a3ba425911cc..2a24488fad80 100644 --- a/sound/pci/ice1712/revo.h +++ b/sound/pci/ice1712/revo.h @@ -34,7 +34,7 @@ #define VT1724_SUBDEVICE_AUDIOPHILE192 0x12143236 /* entry point */ -extern struct snd_ice1712_card_info snd_vt1724_revo_cards[]; +extern const struct snd_ice1712_card_info snd_vt1724_revo_cards[]; /* diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 239524158fe7..72b060d63c29 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -56,7 +56,7 @@ static int __devinit k8x800_add_controls(struct snd_ice1712 *ice) /* EEPROM image */ -static unsigned char k8x800_eeprom[] __devinitdata = { +static const unsigned char k8x800_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */ [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */ [ICE_EEP2_I2S] = 0x00, /* - */ @@ -72,7 +72,7 @@ static unsigned char k8x800_eeprom[] __devinitdata = { [ICE_EEP2_GPIO_STATE2] = 0x00, /* - */ }; -static unsigned char sn25p_eeprom[] __devinitdata = { +static const unsigned char sn25p_eeprom[] __devinitdata = { [ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */ [ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */ [ICE_EEP2_I2S] = 0x00, /* - */ @@ -90,7 +90,7 @@ static unsigned char sn25p_eeprom[] __devinitdata = { /* entry point */ -struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = { +const struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = { { .subvendor = VT1720_SUBDEVICE_K8X800, .name = "Albatron K8X800 Pro II", diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h index 0b1b0ee1bea7..70af3ad64a5d 100644 --- a/sound/pci/ice1712/vt1720_mobo.h +++ b/sound/pci/ice1712/vt1720_mobo.h @@ -36,6 +36,6 @@ #define VT1720_SUBDEVICE_9CJS 0x0f272327 #define VT1720_SUBDEVICE_SN25P 0x97123650 -extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[]; +extern const struct snd_ice1712_card_info snd_vt1720_mobo_cards[]; #endif /* __SOUND_VT1720_MOBO_H */ diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index 04e535c8542b..4a706b16a0b9 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -409,7 +409,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, /* * Control tabs */ -static struct snd_kcontrol_new stac9640_controls[] __devinitdata = { +static const struct snd_kcontrol_new stac9640_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", -- cgit v1.2.3-59-g8ed1b From 517400cbc75d0604bc34c1866dff7e55ca1be2b4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jan 2007 15:27:56 +0100 Subject: [ALSA] Add some more 'const', but needs changes in i2c/other/ak4* Make data passed to ak4xxx_create 'const'. Signed-of-by: Philipp Matthias Hahn Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ak4114.h | 2 +- include/sound/ak4117.h | 2 +- include/sound/ak4xxx-adda.h | 4 ++-- sound/i2c/other/ak4114.c | 2 +- sound/i2c/other/ak4117.c | 2 +- sound/i2c/other/ak4xxx-adda.c | 15 ++++++++------- sound/pci/ice1712/juli.c | 4 ++-- sound/pci/ice1712/revo.c | 14 +++++++------- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index 85f49d464751..c149d3b2558b 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -188,7 +188,7 @@ struct ak4114 { int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - unsigned char pgm[7], unsigned char txcsb[5], + const unsigned char pgm[7], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114); void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val); void snd_ak4114_reinit(struct ak4114 *ak4114); diff --git a/include/sound/ak4117.h b/include/sound/ak4117.h index 2b96c32f06fd..d650d52e3d29 100644 --- a/include/sound/ak4117.h +++ b/include/sound/ak4117.h @@ -178,7 +178,7 @@ struct ak4117 { }; int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write, - unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117); + const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117); void snd_ak4117_reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char mask, unsigned char val); void snd_ak4117_reinit(struct ak4117 *ak4117); int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *capture_substream); diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index d01d53528015..aa49dda4f410 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -71,8 +71,8 @@ struct snd_akm4xxx { } type; /* (array) information of combined codecs */ - struct snd_akm4xxx_dac_channel *dac_info; - struct snd_akm4xxx_adc_channel *adc_info; + const struct snd_akm4xxx_dac_channel *dac_info; + const struct snd_akm4xxx_adc_channel *adc_info; struct snd_ak4xxx_ops ops; }; diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 69dcaf8ac793..34bbafc81cf6 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -79,7 +79,7 @@ static int snd_ak4114_dev_free(struct snd_device *device) int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - unsigned char pgm[7], unsigned char txcsb[5], + const unsigned char pgm[7], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114) { struct ak4114 *chip; diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c index 4e45952dd95a..c022f29da2f7 100644 --- a/sound/i2c/other/ak4117.c +++ b/sound/i2c/other/ak4117.c @@ -74,7 +74,7 @@ static int snd_ak4117_dev_free(struct snd_device *device) } int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write, - unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117) + const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117) { struct ak4117 *chip; int err = 0; diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index fe61b92f4e47..3d9d6c5d354e 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -140,7 +140,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset); * Used for AK4524 input/ouput attenuation, AK4528, and * AK5365 input attenuation */ -static unsigned char vol_cvt_datt[128] = { +static const unsigned char vol_cvt_datt[128] = { 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a, @@ -184,7 +184,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x07, 0x00, /* 7: DAC right muted */ 0xff, 0xff }; - static unsigned char inits_ak4528[] = { + static const unsigned char inits_ak4528[] = { 0x00, 0x07, /* 0: all power up */ 0x01, 0x00, /* 1: ADC/DAC reset */ 0x02, 0x60, /* 2: 24bit I2S */ @@ -194,7 +194,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x05, 0x00, /* 5: ADC right muted */ 0xff, 0xff }; - static unsigned char inits_ak4529[] = { + static const unsigned char inits_ak4529[] = { 0x09, 0x01, /* 9: ATS=0, RSTN=1 */ 0x0a, 0x3f, /* A: all power up, no zero/overflow detection */ 0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */ @@ -210,7 +210,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x08, 0x55, /* 8: deemphasis all off */ 0xff, 0xff }; - static unsigned char inits_ak4355[] = { + static const unsigned char inits_ak4355[] = { 0x01, 0x02, /* 1: reset and soft-mute */ 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, * disable DZF, sharp roll-off, RSTN#=0 */ @@ -227,7 +227,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x01, 0x01, /* 1: un-reset, unmute */ 0xff, 0xff }; - static unsigned char inits_ak4358[] = { + static const unsigned char inits_ak4358[] = { 0x01, 0x02, /* 1: reset and soft-mute */ 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, * disable DZF, sharp roll-off, RSTN#=0 */ @@ -246,7 +246,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x01, 0x01, /* 1: un-reset, unmute */ 0xff, 0xff }; - static unsigned char inits_ak4381[] = { + static const unsigned char inits_ak4381[] = { 0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */ 0x01, 0x02, /* 1: de-emphasis off, normal speed, * sharp roll-off, DZF off */ @@ -259,7 +259,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) }; int chip, num_chips; - unsigned char *ptr, reg, data, *inits; + const unsigned char *ptr, *inits; + unsigned char reg, data; memset(ak->images, 0, sizeof(ak->images)); memset(ak->volumes, 0, sizeof(ak->volumes)); diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 4854eaf63a8a..d88172fa95da 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -146,7 +146,7 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) */ static int __devinit juli_init(struct snd_ice1712 *ice) { - static unsigned char ak4114_init_vals[] = { + static const unsigned char ak4114_init_vals[] = { /* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1, /* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S, /* AK4114_REG_IO0 */ AK4114_TX1E, @@ -154,7 +154,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice) /* AK4114_REG_INT0_MASK */ 0, /* AK4114_REG_INT1_MASK */ 0 }; - static unsigned char ak4114_init_txcsb[] = { + static const unsigned char ak4114_init_txcsb[] = { 0x41, 0x02, 0x2c, 0x00, 0x00 }; int err; diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 7d3bccbf0313..025a7e8497c3 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -185,18 +185,18 @@ static int revo51_i2c_init(struct snd_ice1712 *ice, #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch } -static struct snd_akm4xxx_dac_channel revo71_front[] = { +static const struct snd_akm4xxx_dac_channel revo71_front[] = { AK_DAC("PCM Playback Volume", 2) }; -static struct snd_akm4xxx_dac_channel revo71_surround[] = { +static const struct snd_akm4xxx_dac_channel revo71_surround[] = { AK_DAC("PCM Center Playback Volume", 1), AK_DAC("PCM LFE Playback Volume", 1), AK_DAC("PCM Side Playback Volume", 2), AK_DAC("PCM Rear Playback Volume", 2), }; -static struct snd_akm4xxx_dac_channel revo51_dac[] = { +static const struct snd_akm4xxx_dac_channel revo51_dac[] = { AK_DAC("PCM Playback Volume", 2), AK_DAC("PCM Center Playback Volume", 1), AK_DAC("PCM LFE Playback Volume", 1), @@ -210,7 +210,7 @@ static const char *revo51_adc_input_names[] = { NULL }; -static struct snd_akm4xxx_adc_channel revo51_adc[] = { +static const struct snd_akm4xxx_adc_channel revo51_adc[] = { { .name = "PCM Capture Volume", .switch_name = "PCM Capture Switch", @@ -219,7 +219,7 @@ static struct snd_akm4xxx_adc_channel revo51_adc[] = { }, }; -static struct snd_akm4xxx akm_revo_front __devinitdata = { +static const struct snd_akm4xxx akm_revo_front __devinitdata = { .type = SND_AK4381, .num_dacs = 2, .ops = { @@ -320,11 +320,11 @@ static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) #endif } -static struct snd_akm4xxx_dac_channel ap192_dac[] = { +static const struct snd_akm4xxx_dac_channel ap192_dac[] = { AK_DAC("PCM Playback Volume", 2) }; -static struct snd_akm4xxx akm_ap192 __devinitdata = { +static const struct snd_akm4xxx akm_ap192 __devinitdata = { .type = SND_AK4358, .num_dacs = 2, .ops = { -- cgit v1.2.3-59-g8ed1b From 0cb29ea0d449d7c0ecc9649a08ab63476389701d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jan 2007 15:33:49 +0100 Subject: [ALSA] Add even more 'const' to everything related to TLV Mark TLV data as 'const' Signed-of-by: Philipp Matthias Hahn Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ad1848.h | 2 +- include/sound/control.h | 4 ++-- include/sound/emu10k1.h | 2 +- include/sound/vx_core.h | 2 +- sound/drivers/dummy.c | 2 +- sound/drivers/vx/vx_mixer.c | 2 +- sound/i2c/other/ak4xxx-adda.c | 10 +++++----- sound/i2c/other/pt2258.c | 2 +- sound/isa/ad1816a/ad1816a_lib.c | 10 +++++----- sound/isa/ad1848/ad1848_lib.c | 6 +++--- sound/isa/opl3sa2.c | 4 ++-- sound/pci/ac97/ac97_codec.c | 16 ++++++++-------- sound/pci/ac97/ac97_patch.c | 6 +++--- sound/pci/ac97/ak4531_codec.c | 6 +++--- sound/pci/ca0106/ca0106_mixer.c | 4 ++-- sound/pci/cs4281.c | 2 +- sound/pci/echoaudio/echoaudio.c | 4 ++-- sound/pci/emu10k1/emufx.c | 4 ++-- sound/pci/emu10k1/emumixer.c | 2 +- sound/pci/emu10k1/p16v.c | 2 +- sound/pci/es1938.c | 2 +- sound/pci/fm801.c | 2 +- sound/pci/ice1712/aureon.c | 10 +++++----- sound/pci/ice1712/ice1712.c | 2 +- sound/pci/ice1712/phase.c | 4 ++-- sound/pci/ice1712/pontis.c | 2 +- sound/pci/ice1712/prodigy192.c | 4 ++-- sound/pci/mixart/mixart_mixer.c | 4 ++-- sound/pci/pcxhr/pcxhr_mixer.c | 6 +++--- sound/pci/trident/trident_main.c | 4 ++-- sound/pci/via82xx.c | 2 +- sound/pci/vx222/vx222.c | 4 ++-- sound/pci/vx222/vx222_ops.c | 2 +- sound/pci/ymfpci/ymfpci_main.c | 2 +- sound/pcmcia/vx/vxp_mixer.c | 2 +- sound/pcmcia/vx/vxpocket.c | 2 +- 36 files changed, 73 insertions(+), 73 deletions(-) diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h index c8de6f83338f..b2c3f00a9b35 100644 --- a/include/sound/ad1848.h +++ b/include/sound/ad1848.h @@ -185,7 +185,7 @@ struct ad1848_mix_elem { int index; int type; unsigned long private_value; - unsigned int *tlv; + const unsigned int *tlv; }; #define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ diff --git a/include/sound/control.h b/include/sound/control.h index f1361d6694ff..72e759f619b1 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -49,7 +49,7 @@ struct snd_kcontrol_new { snd_kcontrol_put_t *put; union { snd_kcontrol_tlv_rw_t *c; - unsigned int *p; + const unsigned int *p; } tlv; unsigned long private_value; }; @@ -69,7 +69,7 @@ struct snd_kcontrol { snd_kcontrol_put_t *put; union { snd_kcontrol_tlv_rw_t *c; - unsigned int *p; + const unsigned int *p; } tlv; unsigned long private_value; void *private_data; diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 975df288ce49..eb7ce96ddf3a 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1903,7 +1903,7 @@ struct snd_emu10k1_fx8010_control_gpr { unsigned int min; /* minimum range */ unsigned int max; /* maximum range */ unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ - unsigned int *tlv; + const unsigned int *tlv; }; /* old ABI without TLV support */ diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h index 217394652090..4830651cc4cf 100644 --- a/include/sound/vx_core.h +++ b/include/sound/vx_core.h @@ -128,7 +128,7 @@ struct snd_vx_hardware { unsigned int num_ins; unsigned int num_outs; unsigned int output_level_max; - unsigned int *output_level_db_scale; + const unsigned int *output_level_db_scale; }; /* hwdep id string */ diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 42001efa9f3e..8339bad969ba 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -501,7 +501,7 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol, return change; } -static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0); #define DUMMY_CAPSRC(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 1613ed844ac6..f63152a6a223 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -716,7 +716,7 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return 0; } -static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0); static struct snd_kcontrol_new vx_control_audio_gain = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 3d9d6c5d354e..8805110017a7 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -162,17 +162,17 @@ static const unsigned char vol_cvt_datt[128] = { /* * dB tables */ -static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1); -static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1); -static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1); -static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1); +static const DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0); /* * initialize all the ak4xxx chips */ void snd_akm4xxx_init(struct snd_akm4xxx *ak) { - static unsigned char inits_ak4524[] = { + static const unsigned char inits_ak4524[] = { 0x00, 0x07, /* 0: all power up */ 0x01, 0x00, /* 1: ADC/DAC reset */ 0x02, 0x60, /* 2: 24bit I2S */ diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c index 50df1df2f2b9..e91cc3b44de5 100644 --- a/sound/i2c/other/pt2258.c +++ b/sound/i2c/other/pt2258.c @@ -185,7 +185,7 @@ static int pt2258_switch_put(struct snd_kcontrol *kcontrol, return -EIO; } -static DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0); +static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0); int snd_pt2258_build_controls(struct snd_pt2258 *pt) { diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index b524e0d9ee44..ec9209cd5177 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -906,11 +906,11 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ return change; } -static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = { AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index 666b3bcc19f0..8094282c2ae1 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -1223,9 +1223,9 @@ int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, EXPORT_SYMBOL(snd_ad1848_add_ctl_elem); -static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); static struct ad1848_mix_elem snd_ad1848_controls[] = { AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 419b4ebbf00e..1e30713d2cad 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -486,8 +486,8 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ return change; } -static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); static struct snd_kcontrol_new snd_opl3sa2_controls[] = { OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index bd8cfdcfbdf1..8b7853c14b5b 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1190,13 +1190,13 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, /* * set dB information */ -static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); -static unsigned int *find_db_scale(unsigned int maxval) +static const unsigned int *find_db_scale(unsigned int maxval) { switch (maxval) { case 0x0f: return db_scale_4bit; @@ -1206,8 +1206,8 @@ static unsigned int *find_db_scale(unsigned int maxval) return NULL; } -static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv) -{ +static void set_tlv_db_scale(struct snd_kcontrol *kctl, const unsigned int *tlv) +{ kctl->tlv.p = tlv; if (tlv) kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index f1950fa1f0ef..641d0c8d659e 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -54,7 +54,7 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro /* replace with a new TLV */ static void reset_tlv(struct snd_ac97 *ac97, const char *name, - unsigned int *tlv) + const unsigned int *tlv) { struct snd_ctl_elem_id sid; struct snd_kcontrol *kctl; @@ -1569,7 +1569,7 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = { AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */ }; -static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0); static int patch_ad1885_specific(struct snd_ac97 * ac97) { @@ -2527,7 +2527,7 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = { /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */ }; -static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0); static int patch_alc650_specific(struct snd_ac97 * ac97) { diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index c153cb79c518..dc26820a03a5 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -267,9 +267,9 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl return change; } -static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0); -static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0); -static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0); static struct snd_kcontrol_new snd_ak4531_controls[] = { diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 289f78a41608..b913a1fb8c21 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -74,8 +74,8 @@ #include "ca0106.h" -static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); -static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); +static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); +static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 8e5519de7115..44cf54607647 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1055,7 +1055,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol, return change; } -static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0); static struct snd_kcontrol_new snd_cs4281_fm_vol = { diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 3410bd4450ad..6a428b81dba6 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -34,7 +34,7 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; -static DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1); static int get_firmware(const struct firmware **fw_entry, const struct firmware *frm, struct echoaudio *chip) @@ -1085,7 +1085,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol, return changed; } -static DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0); static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = { .name = "Line Capture Volume", diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 7b173c496953..c02012cccd8e 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -296,7 +296,7 @@ static const u32 db_table[101] = { }; /* EMU10k1/EMU10k2 DSP control db gain */ -static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1); +static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1); static const u32 onoff_table[2] = { 0x00000000, 0x00000001 @@ -657,7 +657,7 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id) #define MAX_TLV_SIZE 256 -static unsigned int *copy_tlv(unsigned int __user *_tlv) +static unsigned int *copy_tlv(const unsigned int __user *_tlv) { unsigned int data[2]; unsigned int *tlv; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 0469546fc333..0981af842b76 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -42,7 +42,7 @@ #define AC97_ID_STAC9758 0x83847658 -static DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */ +static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */ static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 5da637c73393..465f8d505329 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -785,7 +785,7 @@ static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol, } return change; } -static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1); +static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1); #define P16V_VOL(xname,xreg,xhl) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 66ac26c5a240..fec29a108945 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1344,7 +1344,7 @@ static unsigned int db_scale_line[] = { 8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0), }; -static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0); static struct snd_kcontrol_new snd_es1938_controls[] = { ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0, diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index b7b361ce3a93..6dc578bbeec9 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1157,7 +1157,7 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol, return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); } -static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0); #define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 625a9a32b7c7..6941d85dfec9 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -664,11 +664,11 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } -static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); -static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); -static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0); -static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0); -static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0); /* * Logarithmic volume values for WM8770 diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index b8baadba810c..830a1bbd7110 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1378,7 +1378,7 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc return change; } -static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0); static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = { { diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 2d97ac8a07d3..0751718f4d7b 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -697,8 +697,8 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ct return 0; } -static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); -static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); static const struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = { { diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 4c35ddecb8e6..9552497f0765 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -565,7 +565,7 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el return changed; } -static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1); /* * mixers diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index d551e71c67ca..31cc66eb9f8f 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -357,8 +357,8 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl } #endif -static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); -static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); /* * mixers diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 13de0f71d4b7..d7d15c036e02 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -389,7 +389,7 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e return changed; } -static DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0); static struct snd_kcontrol_new mixart_control_analog_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -872,7 +872,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem return changed; } -static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); static struct snd_kcontrol_new snd_mixart_pcm_vol = { diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index b133ad9e095e..d9cc8d2beb6d 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -44,8 +44,8 @@ #define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */ #define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */ -static DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0); -static DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0); static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel) { @@ -195,7 +195,7 @@ static struct snd_kcontrol_new pcxhr_control_output_switch = { #define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */ #define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */ -static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); #define MORE_THAN_ONE_STREAM_LEVEL 0x000001 #define VALID_STREAM_PAN_LEVEL_MASK 0x800000 diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 474f2d451ae8..3bff32167f66 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2627,7 +2627,7 @@ static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol, return 0; } -static DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0); static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2844,7 +2844,7 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol, return change; } -static DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1); static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata = { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 22caf5d7ff1e..a28992269f5e 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1699,7 +1699,7 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol, return change; } -static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1); static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = { .name = "PCM Playback Volume", diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 89f58ea180b3..474eac9490ae 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -73,8 +73,8 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids); /* */ -static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); -static DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0); static struct snd_vx_hardware vx222_old_hw = { diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 5e51950e05f9..55558bef7166 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -846,7 +846,7 @@ static void vx2_set_input_level(struct snd_vx222 *chip) #define MIC_LEVEL_MAX 0xff -static DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0); /* * controls API for input levels diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 8b076932f4f5..fd12674d0394 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1504,7 +1504,7 @@ static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol, return change; } -static DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0); +static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0); #define YMFPCI_DOUBLE(xname, xindex, reg) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c index bced7b623b12..2b1f996c898d 100644 --- a/sound/pcmcia/vx/vxp_mixer.c +++ b/sound/pcmcia/vx/vxp_mixer.c @@ -64,7 +64,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return 0; } -static DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0); static struct snd_kcontrol_new vx_control_mic_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index d7df59e9c647..363bcb5f08e6 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -91,7 +91,7 @@ static int snd_vxpocket_dev_free(struct snd_device *device) * Only output levels can be modified */ -static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); static struct snd_vx_hardware vxpocket_hw = { .name = "VXPocket", -- cgit v1.2.3-59-g8ed1b From 18b9b3d99677a758e77682d6849f58fc07e30bef Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 30 Jan 2007 17:18:45 +0100 Subject: [ALSA] ASoC codec probe failure bug This patch fixes a bug whereby some resources were not being freed when codec probe() failed. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cf84d8251715..736949fbb4d5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1234,7 +1234,7 @@ platform_err: codec_dev->remove(pdev); cpu_dai_err: - for (i--; i > 0; i--) { + for (i--; i >= 0; i--) { struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->remove) cpu_dai->remove(pdev); -- cgit v1.2.3-59-g8ed1b From 877b866d86786ac69d3d939905999fe7fe1e23fd Mon Sep 17 00:00:00 2001 From: "Cory T. Tusar" Date: Tue, 30 Jan 2007 17:30:55 +0100 Subject: [ALSA] hda-codec - Dell Latitude D820 + D/Port Support port replicator headphone output on Dell Latitude D820 + D/Port. Signed-off-by: Cory T. Tusar Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0556b7e7bb8b..6f4a39273b98 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -455,6 +455,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { "Dell Latitude D620", STAC_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb, "Dell Latitude 120L", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc, + "Dell Latitude D820", STAC_REF), {} /* terminator */ }; -- cgit v1.2.3-59-g8ed1b From e35115a58856ced315cb8f75df56e9b9a816e70a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 31 Jan 2007 10:02:23 +0100 Subject: [ALSA] ASoC codec error reporting This patch improves the codec probe() error reporting by printing error messages when the card or pcms fail to register. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8731.c | 15 +++++++++++---- sound/soc/codecs/wm8750.c | 14 ++++++++++---- sound/soc/codecs/wm9712.c | 22 +++++++++++++--------- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 8151b45a280c..9956d654b6f3 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -696,8 +696,8 @@ static int wm8731_init(struct snd_soc_device *socdev) /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - kfree(codec->reg_cache); - return ret; + printk(KERN_ERR "wm8731: failed to create pcms\n"); + goto pcm_err; } /* power on device */ @@ -717,11 +717,18 @@ static int wm8731_init(struct snd_soc_device *socdev) wm8731_add_widgets(codec); ret = snd_soc_register_card(socdev); if (ret < 0) { - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); + printk(KERN_ERR "wm8731: failed to register card\n"); + goto card_err; } return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + return ret; } static struct snd_soc_device *wm8731_socdev; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index e7f04b89c8a0..d4a288ba644b 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -1075,8 +1075,8 @@ static int wm8750_init(struct snd_soc_device *socdev) /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - kfree(codec->reg_cache); - return ret; + printk(KERN_ERR "wm8750: failed to create pcms\n"); + goto pcm_err; } /* charge output caps */ @@ -1106,10 +1106,16 @@ static int wm8750_init(struct snd_soc_device *socdev) wm8750_add_widgets(codec); ret = snd_soc_register_card(socdev); if (ret < 0) { - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); + printk(KERN_ERR "wm8750: failed to register card\n"); + goto card_err; } + return ret; +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); return ret; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 36c6a38a0f94..b2d2d03b9544 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -692,10 +692,8 @@ static int wm9712_soc_probe(struct platform_device *pdev) codec->reg_cache = kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL); if (codec->reg_cache == NULL) { - kfree(codec->ac97); - kfree(socdev->codec); - socdev->codec = NULL; - return -ENOMEM; + ret = -ENOMEM; + goto cache_err; } memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg)); codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg); @@ -712,8 +710,10 @@ static int wm9712_soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_paths); ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); - if (ret < 0) - goto err; + if (ret < 0) { + printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); + goto codec_err; + } /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); @@ -733,8 +733,10 @@ static int wm9712_soc_probe(struct platform_device *pdev) wm9712_add_controls(codec); wm9712_add_widgets(codec); ret = snd_soc_register_card(socdev); - if (ret < 0) + if (ret < 0) { + printk(KERN_ERR "wm9712: failed to register card\n"); goto reset_err; + } return 0; @@ -744,8 +746,10 @@ reset_err: pcm_err: snd_soc_free_ac97_codec(codec); -err: - kfree(socdev->codec->reg_cache); +codec_err: + kfree(codec->reg_cache); + +cache_err: kfree(socdev->codec); socdev->codec = NULL; return ret; -- cgit v1.2.3-59-g8ed1b From 298a2c753a5ae2f0e230a57e94843d248f0033e2 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Wed, 31 Jan 2007 10:02:56 +0100 Subject: [ALSA] ASoC WM8731 support for 32k @ 12MHz sysclk This patch adds support for 32k audio on the WM8731 when running from a 12MHz system clock. Signed-off-by: Frank Mandarino Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8731.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9956d654b6f3..82f440fcf294 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -435,6 +435,7 @@ static const struct _coeff_div coeff_div[] = { /* 32k */ {12288000, 32000, 384, 0x6, 0x0, 0x0}, {18432000, 32000, 576, 0x6, 0x1, 0x0}, + {12000000, 32000, 375, 0x6, 0x0, 0x1}, /* 8k */ {12288000, 8000, 1536, 0x3, 0x0, 0x0}, -- cgit v1.2.3-59-g8ed1b From 8fa58af7db56077d6a042fd7b9dd4c9515e1c37b Mon Sep 17 00:00:00 2001 From: Karsten Wiese Date: Wed, 31 Jan 2007 10:05:30 +0100 Subject: [ALSA] snd_hwdep_release() racefix snd_card_file_remove() can free the snd_card. Touch hw->* only before calling snd_card_file_remove(). Unrelated: Allow hwdep devices not to have own ops.release(); Signed-off-by: Karsten Wiese Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/hwdep.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index a6a6ad0ad3c8..39c03f3dfbfa 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -156,15 +156,16 @@ static int snd_hwdep_release(struct inode *inode, struct file * file) int err = -ENXIO; struct snd_hwdep *hw = file->private_data; struct module *mod = hw->card->module; + mutex_lock(&hw->open_mutex); - if (hw->ops.release) { + if (hw->ops.release) err = hw->ops.release(hw, file); - wake_up(&hw->open_wait); - } if (hw->used > 0) hw->used--; - snd_card_file_remove(hw->card, file); mutex_unlock(&hw->open_mutex); + wake_up(&hw->open_wait); + + snd_card_file_remove(hw->card, file); module_put(mod); return err; } -- cgit v1.2.3-59-g8ed1b From 4147dab62d1b4387c304888488e1f67a83ad53c8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 31 Jan 2007 10:35:19 +0100 Subject: [ALSA] hda-codec - Add model for Uniwill X40AIx Added model=uniwill-m31 for Uniwill X40AIx with ALC861 codec. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d01895515e0d..c022e8157c34 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7758,6 +7758,7 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), {} }; -- cgit v1.2.3-59-g8ed1b From beb60119bcc9cbd80bc5b4f7feec419e067d3e46 Mon Sep 17 00:00:00 2001 From: Gregor Jasny Date: Wed, 31 Jan 2007 12:27:39 +0100 Subject: [ALSA] usbaudio - Fix Oops with broken usb descriptors This is a patch for ALSA Bug #2724. Some webcams provide bogus settings with no valid rates. With this patch those are skipped. Signed-off-by: Gregor Jasny Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index de680d095e94..56e42b88addf 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -2463,6 +2463,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform * build the rate table and bitmap flags */ int r, idx, c; + unsigned int nonzero_rates = 0; /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */ static unsigned int conv_rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, @@ -2485,6 +2486,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform fp->altsetting == 5 && fp->maxpacksize == 392) rate = 96000; fp->rate_table[r] = rate; + nonzero_rates |= rate; if (rate < fp->rate_min) fp->rate_min = rate; else if (rate > fp->rate_max) @@ -2500,6 +2502,10 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform if (!found) fp->needs_knot = 1; } + if (!nonzero_rates) { + hwc_debug("All rates were zero. Skipping format!\n"); + return -1; + } if (fp->needs_knot) fp->rates |= SNDRV_PCM_RATE_KNOT; } else { -- cgit v1.2.3-59-g8ed1b From 965ac42ce919db225ee64678f0be02f2fdf5b5e4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 31 Jan 2007 14:14:57 +0100 Subject: [ALSA] ASoC force running of delayed PM work at suspend() and remove() This patch fixes a bug whereby the power management delayed work would never be run at driver suspend() or module remove(). Delayed work would be created (after audio had finished) with a long delay (~5 secs) and was sometimes never queued before flush_scheduled_work() was being called at suspend or module remove. This caused the delayed work to queued after the module had been removed or after resume. This patch forces any delayed work to complete by cancelling it (timer cannot fire and add it to queue later), scheduling it for now and waiting on it's completion. This is something I probably would like to add to workqueue.c in the next merge window, however it's here atm because it can oops. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 736949fbb4d5..e5aa1c20dddb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -77,6 +77,25 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +/* + * This function forces any delayed work to be queued and run. + */ +static int run_delayed_work(struct delayed_work *dwork) +{ + int ret; + + /* cancel any work waiting to be queued. */ + ret = cancel_delayed_work(dwork); + + /* if there was any work waiting then we run it now and + * wait for it's completion */ + if (ret) { + schedule_delayed_work(dwork, 0); + flush_scheduled_work(); + } + return ret; +} + #ifdef CONFIG_SND_SOC_AC97_BUS /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) @@ -1101,7 +1120,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) } /* close any waiting streams and save state */ - flush_scheduled_work(); + run_delayed_work(&socdev->delayed_work); codec->suspend_dapm_state = codec->dapm_state; for(i = 0; i < codec->num_dai; i++) { @@ -1255,6 +1274,8 @@ static int soc_remove(struct platform_device *pdev) struct snd_soc_platform *platform = socdev->platform; struct snd_soc_codec_device *codec_dev = socdev->codec_dev; + run_delayed_work(&socdev->delayed_work); + if (platform->remove) platform->remove(pdev); -- cgit v1.2.3-59-g8ed1b From 3b6baa5a0b0a2877c18a76fa1f508cacdbc08edf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 31 Jan 2007 14:34:38 +0100 Subject: [ALSA] Remove delayed work properly at free and suspend Remove delayed work properly at free and suspend in ac97 codec and ak4114 drivers. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/i2c/other/ak4114.c | 3 ++- sound/pci/ac97/ac97_codec.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 34bbafc81cf6..d2b17c83fd33 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -66,6 +66,7 @@ static void snd_ak4114_free(struct ak4114 *chip) { chip->init = 1; /* don't schedule new work */ mb(); + cancel_delayed_work(&chip->work); flush_scheduled_work(); kfree(chip); } @@ -97,6 +98,7 @@ int snd_ak4114_create(struct snd_card *card, chip->read = read; chip->write = write; chip->private_data = private_data; + INIT_DELAYED_WORK(&chip->work, ak4114_stats); for (reg = 0; reg < 7; reg++) chip->regmap[reg] = pgm[reg]; @@ -149,7 +151,6 @@ void snd_ak4114_reinit(struct ak4114 *chip) reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN); /* bring up statistics / event queing */ chip->init = 0; - INIT_DELAYED_WORK(&chip->work, ak4114_stats); schedule_delayed_work(&chip->work, HZ / 10); } diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 8b7853c14b5b..74ed81081478 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -990,6 +990,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97) if (ac97) { #ifdef CONFIG_SND_AC97_POWER_SAVE cancel_delayed_work(&ac97->power_work); + flush_scheduled_work(); #endif snd_ac97_proc_done(ac97); if (ac97->bus) @@ -2415,6 +2416,10 @@ void snd_ac97_suspend(struct snd_ac97 *ac97) return; if (ac97->build_ops->suspend) ac97->build_ops->suspend(ac97); +#ifdef CONFIG_SND_AC97_POWER_SAVE + cancel_delayed_work(&ac97->power_work); + flush_scheduled_work(); +#endif snd_ac97_powerdown(ac97); } -- cgit v1.2.3-59-g8ed1b From 8fec560d9beb3957bf45ac93b1c0c616abd77a07 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Feb 2007 11:50:56 +0100 Subject: [ALSA] usbaudio - Fix Oops with unconventional sample rates The patch fixes the memory corruption by the support of unconventional sample rates. Also, it avoids the too restrictive constraints if any of usb descriptions contain continuous rates. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 56e42b88addf..8fd37596e3a1 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -186,6 +186,7 @@ struct snd_usb_substream { u64 formats; /* format bitmasks (all or'ed) */ unsigned int num_formats; /* number of supported audio formats (list) */ struct list_head fmt_list; /* format list */ + struct snd_pcm_hw_constraint_list rate_list; /* limited rates */ spinlock_t lock; struct snd_urb_ops ops; /* callbacks (must be filled at init) */ @@ -1818,28 +1819,33 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { - struct list_head *p; - struct snd_pcm_hw_constraint_list constraints_rates; + struct audioformat *fp; + int count = 0, needs_knot = 0; int err; - list_for_each(p, &subs->fmt_list) { - struct audioformat *fp; - fp = list_entry(p, struct audioformat, list); - - if (!fp->needs_knot) - continue; - - constraints_rates.count = fp->nr_rates; - constraints_rates.list = fp->rate_table; - constraints_rates.mask = 0; - - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - - if (err < 0) - return err; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) + return 0; + count += fp->nr_rates; + if (fp->needs_knot) + needs_knot = 1; } + if (!needs_knot) + return 0; + + subs->rate_list.count = count; + subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); + subs->rate_list.mask = 0; + count = 0; + list_for_each_entry(fp, &subs->fmt_list, list) { + int i; + for (i = 0; i < fp->nr_rates; i++) + subs->rate_list.list[count++] = fp->rate_table[i]; + } + err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &subs->rate_list); + if (err < 0) + return err; return 0; } @@ -2238,6 +2244,7 @@ static void free_substream(struct snd_usb_substream *subs) kfree(fp->rate_table); kfree(fp); } + kfree(subs->rate_list.list); } -- cgit v1.2.3-59-g8ed1b From bc7320c5c8ddeb3b50c6a24013dab9ba74bce578 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 1 Feb 2007 12:26:07 +0100 Subject: [ALSA] ASoC very minor coding style fix for snd_soc_new_pcms() This very minor patch fixes the snd_soc_new_pcms() function to comply with the kernel coding style. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e5aa1c20dddb..87be938622dd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1522,7 +1522,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_rate); * * Returns 0 for success, else error. */ -int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char * xid) +int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) { struct snd_soc_codec *codec = socdev->codec; struct snd_soc_machine *machine = socdev->machine; -- cgit v1.2.3-59-g8ed1b From 0981a260a1fe4a3f22cc70ef01ce38a73f548745 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Feb 2007 14:53:49 +0100 Subject: [ALSA] Fix possible invalid memory access in PCM core snd_internval_list() may access invalid memory in the case count = 0 is given. It shouldn't be passed, but it'd better to make the code a bit more robust. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/pcm_lib.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index b336797be4fc..9fefcaa2c324 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -781,6 +781,11 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int * { unsigned int k; int changed = 0; + + if (!count) { + i->empty = 1; + return -EINVAL; + } for (k = 0; k < count; k++) { if (mask && !(mask & (1 << k))) continue; -- cgit v1.2.3-59-g8ed1b From 3372a153c230bd0b28d470118d5a4c5840f8f966 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Feb 2007 15:46:50 +0100 Subject: [ALSA] hda-intel - Add black/whitelist for position_fix option Some devices are known to require position_fix=1 or 2 to make the driver working correctly. Otherwise the sound gets weird effects, such as stutters. Now a black/whitelist is introduced to indicate the position_fix value explicitly for such misbehaving hardwares. As a first example, Dell D820 is listed there. More will come later likely... Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 83d1ba7f33a0..b9a8e238b0a8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1502,6 +1502,31 @@ static int azx_dev_free(struct snd_device *device) return azx_free(device->device_data); } +/* + * white/black-listing for position_fix + */ +static const struct snd_pci_quirk position_fix_list[] __devinitdata = { + SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE), + {} +}; + +static int __devinit check_position_fix(struct azx *chip, int fix) +{ + const struct snd_pci_quirk *q; + + if (fix == POS_FIX_AUTO) { + q = snd_pci_quirk_lookup(chip->pci, position_fix_list); + if (q) { + snd_printdd(KERN_INFO + "hda_intel: position_fix set to %d " + "for device %04x:%04x\n", + q->value, q->subvendor, q->subdevice); + return q->value; + } + } + return fix; +} + /* * constructor */ @@ -1536,7 +1561,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->driver_type = driver_type; chip->msi = enable_msi; - chip->position_fix = position_fix; + chip->position_fix = check_position_fix(chip, position_fix); + chip->single_cmd = single_cmd; #if BITS_PER_LONG != 64 -- cgit v1.2.3-59-g8ed1b From 1c433fbda4896a6455d97b66a4f2646cbdd52a8c Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Fri, 2 Feb 2007 17:13:05 +0100 Subject: [ALSA] soc - 0.13 ASoC headers This patch updates the API's to include the new DAI configuration and clocking architecture. Changes:- o Removed DAI automatic matching and capabilities structure (struct snd_soc_dai_mode) and macros. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. o Updated version to 0.13 o Added shift to SOC_SINGLE_EXT kcontrol macro. Signed-off-by: Graeme Gregory Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 193 ++++++++++++++++++++++------------------------------ 1 file changed, 83 insertions(+), 110 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index ea836d819ce0..b1dc364b8f74 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -22,7 +22,7 @@ #include #include -#define SND_SOC_VERSION "0.12" +#define SND_SOC_VERSION "0.13.0" /* * Convenience kcontrol builders @@ -60,12 +60,12 @@ .info = snd_soc_info_enum_double, \ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ .private_value = (unsigned long)&xenum } -#define SOC_SINGLE_EXT(xname, xreg, xmask, xinvert,\ +#define SOC_SINGLE_EXT(xname, xreg, xshift, xmask, xinvert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_volsw_ext, \ + .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE_EXT(xreg, xmask, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) } #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_bool_ext, \ @@ -87,20 +87,29 @@ /* * DAI hardware audio formats */ -#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */ -#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */ -#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */ -#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM or LRC */ -#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM or LRC */ -#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */ +#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */ +#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right justified mode */ +#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */ +#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM or LRC */ +#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM or LRC */ +#define SND_SOC_DAIFMT_AC97 5 /* AC97 */ + +#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J +#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J + +/* + * DAI Gating + */ +#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */ +#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated when not Tx/Rx */ /* * DAI hardware signal inversions */ -#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ -#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */ -#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */ -#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */ +#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */ +#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */ +#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */ /* * DAI hardware clock masters @@ -108,58 +117,22 @@ * i.e. if the codec is clk and frm master then the interface is * clk and frame slave. */ -#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */ -#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */ -#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */ -#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */ +#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */ +#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */ +#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */ +#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */ -#define SND_SOC_DAIFMT_FORMAT_MASK 0x00ff +#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f +#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0 #define SND_SOC_DAIFMT_INV_MASK 0x0f00 -#define SND_SOC_DAIFMT_CLOCK_MASK 0xf000 - -/* - * DAI hardware audio direction - */ -#define SND_SOC_DAIDIR_PLAYBACK 0x1 -#define SND_SOC_DAIDIR_CAPTURE 0x2 - -/* - * DAI hardware Time Division Multiplexing (TDM) Slots - * Left and Right data word positions - * This is measured in words (sample size) and not bits. - */ -#define SND_SOC_DAITDM_LRDW(l,r) ((l << 8) | r) - -/* - * DAI hardware clock ratios - * bit clock can either be a generated by dividing mclk or - * by multiplying sample rate, hence there are 2 definitions below - * depending on codec type. - */ -/* ratio of sample rate to mclk/sysclk */ -#define SND_SOC_FS_ALL 0xffff /* all mclk supported */ - -/* bit clock dividers */ -#define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */ -#define SND_SOC_FSBD_REAL(x) (ffs(x)) +#define SND_SOC_DAIFMT_MASTER_MASK 0xf000 -/* bit clock ratio to (sample rate * channels * word size) */ -#define SND_SOC_FSBW(x) (1 << (x - 1)) -#define SND_SOC_FSBW_REAL(x) (ffs(x)) -/* all bclk ratios supported */ -#define SND_SOC_FSB_ALL ~0ULL /* - * DAI hardware flags + * Master Clock Directions */ -/* use bfs mclk divider mode (BCLK = MCLK / x) */ -#define SND_SOC_DAI_BFS_DIV 0x1 -/* use bfs rate mulitplier (BCLK = RATE * x)*/ -#define SND_SOC_DAI_BFS_RATE 0x2 -/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */ -#define SND_SOC_DAI_BFS_RCW 0x4 -/* capture and playback can use different clocks */ -#define SND_SOC_DAI_ASYNC 0x8 +#define SND_SOC_CLOCK_IN 0 +#define SND_SOC_CLOCK_OUT 1 /* * AC97 codec ID's bitmask @@ -195,7 +168,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev); /* set runtime hw params */ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, const struct snd_pcm_hardware *hw); -int snd_soc_get_rate(int rate); /* codec IO */ #define snd_soc_read(codec, reg) codec->read(codec, reg) @@ -244,6 +216,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, /* SoC PCM stream information */ struct snd_soc_pcm_stream { char *stream_name; + u64 formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ unsigned int rate_min; /* min rate */ unsigned int rate_max; /* max rate */ unsigned int channels_min; /* min channels */ @@ -261,23 +235,43 @@ struct snd_soc_ops { int (*trigger)(struct snd_pcm_substream *, int); }; -/* SoC DAI hardware mode */ -struct snd_soc_dai_mode { - u16 fmt; /* SND_SOC_DAIFMT_* */ - u16 tdm; /* SND_SOC_HWTDM_* */ - u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */ - u16 pcmrate; /* SND_SOC_HWRATE_* */ - u16 pcmdir:2; /* SND_SOC_HWDIR_* */ - u16 flags:8; /* hw flags */ - u16 fs; /* mclk to rate divider */ - u64 bfs; /* mclk to bclk dividers */ - unsigned long priv; /* private mode data */ +/* ASoC codec DAI ops */ +struct snd_soc_codec_ops { + /* codec DAI clocking configuration */ + int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai, + int clk_id, unsigned int freq, int dir); + int (*set_pll)(struct snd_soc_codec_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out); + int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai, + int div_id, int div); + + /* CPU DAI format configuration */ + int (*set_fmt)(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt); + int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai, + unsigned int mask, int slots); + int (*set_tristate)(struct snd_soc_codec_dai *, int tristate); + + /* digital mute */ + int (*digital_mute)(struct snd_soc_codec_dai *, int mute); }; -/* DAI capabilities */ -struct snd_soc_dai_cap { - int num_modes; /* number of DAI modes */ - struct snd_soc_dai_mode *mode; /* array of supported DAI modes */ +/* ASoC cpu DAI ops */ +struct snd_soc_cpu_ops { + /* CPU DAI clocking configuration */ + int (*set_sysclk)(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir); + int (*set_clkdiv)(struct snd_soc_cpu_dai *cpu_dai, + int div_id, int div); + int (*set_pll)(struct snd_soc_cpu_dai *cpu_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out); + + /* CPU DAI format configuration */ + int (*set_fmt)(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt); + int (*set_tdm_slot)(struct snd_soc_cpu_dai *cpu_dai, + unsigned int mask, int slots); + int (*set_tristate)(struct snd_soc_cpu_dai *, int tristate); }; /* SoC Codec DAI */ @@ -288,22 +282,16 @@ struct snd_soc_codec_dai { /* DAI capabilities */ struct snd_soc_pcm_stream playback; struct snd_soc_pcm_stream capture; - struct snd_soc_dai_cap caps; /* DAI runtime info */ - struct snd_soc_dai_mode dai_runtime; - struct snd_soc_ops ops; - unsigned int (*config_sysclk)(struct snd_soc_codec_dai*, - struct snd_soc_clock_info *info, unsigned int clk); - int (*digital_mute)(struct snd_soc_codec *, - struct snd_soc_codec_dai*, int); - unsigned int mclk; /* the audio master clock */ - unsigned int pll_in; /* the PLL input clock */ - unsigned int pll_out; /* the PLL output clock */ - unsigned int clk_div; /* internal clock divider << 1 (for fractions) */ + struct snd_soc_codec *codec; unsigned int active; unsigned char pop_wait:1; + /* ops */ + struct snd_soc_ops ops; + struct snd_soc_codec_ops dai_ops; + /* DAI private data */ void *private_data; }; @@ -323,20 +311,18 @@ struct snd_soc_cpu_dai { struct snd_soc_cpu_dai *cpu_dai); int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); - unsigned int (*config_sysclk)(struct snd_soc_cpu_dai *cpu_dai, - struct snd_soc_clock_info *info, unsigned int clk); + + /* ops */ + struct snd_soc_ops ops; + struct snd_soc_cpu_ops dai_ops; /* DAI capabilities */ struct snd_soc_pcm_stream capture; struct snd_soc_pcm_stream playback; - struct snd_soc_dai_cap caps; /* DAI runtime info */ - struct snd_soc_dai_mode dai_runtime; - struct snd_soc_ops ops; struct snd_pcm_runtime *runtime; unsigned char active:1; - unsigned int mclk; void *dma_data; /* DAI private data */ @@ -417,14 +403,12 @@ struct snd_soc_dai_link { /* DAI */ struct snd_soc_codec_dai *codec_dai; struct snd_soc_cpu_dai *cpu_dai; - u32 flags; /* DAI config preference flags */ + + /* machine stream operations */ + struct snd_soc_ops *ops; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_codec *codec); - - /* audio sysclock configuration */ - unsigned int (*config_sysclk)(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info); }; /* SoC machine */ @@ -441,9 +425,6 @@ struct snd_soc_machine { int (*resume_pre)(struct platform_device *pdev); int (*resume_post)(struct platform_device *pdev); - /* machine stream operations */ - struct snd_soc_ops *ops; - /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; int num_links; @@ -462,8 +443,7 @@ struct snd_soc_device { /* runtime channel data */ struct snd_soc_pcm_runtime { - struct snd_soc_codec_dai *codec_dai; - struct snd_soc_cpu_dai *cpu_dai; + struct snd_soc_dai_link *dai; struct snd_soc_device *socdev; }; @@ -478,11 +458,4 @@ struct soc_enum { void *dapm; }; -/* clocking configuration data */ -struct snd_soc_clock_info { - unsigned int rate; - unsigned int fs; - unsigned int bclk_master; -}; - #endif -- cgit v1.2.3-59-g8ed1b From cb666e5bd865cc991c0048d6e81581019a141820 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:13:49 +0100 Subject: [ALSA] soc - ASoC 0.13 core changes This patch updates the ASoC core to the new DAI matching and clocking API in version 0.13 Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. o Added machine driver prepare callback. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-core.c | 781 ++++++++++----------------------------------------- 1 file changed, 148 insertions(+), 633 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 87be938622dd..36519aef55d9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -47,27 +47,11 @@ #else #define dbg(format, arg...) #endif -/* debug DAI capabilities matching */ -#define SOC_DEBUG_DAI 0 -#if SOC_DEBUG_DAI -#define dbgc(format, arg...) printk(format, ## arg) -#else -#define dbgc(format, arg...) -#endif - -#define CODEC_CPU(codec, cpu) ((codec << 4) | cpu) static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(io_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); -/* supported sample rates */ -/* ATTENTION: these values depend on the definition in pcm.h! */ -static const unsigned int rates[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 -}; - /* * This is a timeout to do a DAPM powerdown after a stream is closed(). * It can be used to eliminate pops between different playback streams, e.g. @@ -142,458 +126,6 @@ static inline const char* get_dai_name(int type) return NULL; } -/* get rate format from rate */ -static inline int soc_get_rate_format(int rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(rates); i++) { - if (rates[i] == rate) - return 1 << i; - } - return 0; -} - -/* gets the audio system mclk/sysclk for the given parameters */ -static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_machine *machine = socdev->machine; - int i; - - /* find the matching machine config and get it's mclk for the given - * sample rate and hardware format */ - for(i = 0; i < machine->num_links; i++) { - if (machine->dai_link[i].cpu_dai == rtd->cpu_dai && - machine->dai_link[i].config_sysclk) - return machine->dai_link[i].config_sysclk(rtd, info); - } - return 0; -} - -/* changes a bitclk multiplier mask to a divider mask */ -static u64 soc_bfs_rcw_to_div(u64 bfs, int rate, unsigned int mclk, - unsigned int pcmfmt, unsigned int chn) -{ - int i, j; - u64 bfs_ = 0; - int size = snd_pcm_format_physical_width(pcmfmt), min = 0; - - if (size <= 0) - return 0; - - /* the minimum bit clock that has enough bandwidth */ - min = size * rate * chn; - dbgc("rcw --> div min bclk %d with mclk %d\n", min, mclk); - - for (i = 0; i < 64; i++) { - if ((bfs >> i) & 0x1) { - j = min * (i + 1); - bfs_ |= SND_SOC_FSBD(mclk/j); - dbgc("rcw --> div support mult %d\n", - SND_SOC_FSBD_REAL(1<> i) & 0x1) { - j = mclk / (i + 1); - if (j >= min) { - bfs_ |= SND_SOC_FSBW(j/min); - dbgc("div --> rcw support div %d\n", - SND_SOC_FSBW_REAL(1< rcw min bclk %d with mclk %d\n", min, mclk); - - if (bfs_ < min) - return 0; - else { - bfs_ = SND_SOC_FSBW(bfs_/min); - dbgc("rate --> rcw support div %d\n", SND_SOC_FSBW_REAL(bfs_)); - return bfs_; - } -} - -/* changes a bitclk multiplier mask to a divider mask */ -static u64 soc_bfs_rate_to_div(u64 bfs, int rate, unsigned int mclk, - unsigned int pcmfmt, unsigned int chn) -{ - unsigned int bfs_ = rate * bfs; - int size = snd_pcm_format_physical_width(pcmfmt), min = 0; - - if (size <= 0) - return 0; - - /* the minimum bit clock that has enough bandwidth */ - min = size * rate * chn; - dbgc("rate --> div min bclk %d with mclk %d\n", min, mclk); - - if (bfs_ < min) - return 0; - else { - bfs_ = SND_SOC_FSBW(mclk/bfs_); - dbgc("rate --> div support div %d\n", SND_SOC_FSBD_REAL(bfs_)); - return bfs_; - } -} - -/* Matches codec DAI and SoC CPU DAI hardware parameters */ -static int soc_hw_match_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai_mode *codec_dai_mode = NULL; - struct snd_soc_dai_mode *cpu_dai_mode = NULL; - struct snd_soc_clock_info clk_info; - unsigned int fs, mclk, rate = params_rate(params), - chn, j, k, cpu_bclk, codec_bclk, pcmrate; - u16 fmt = 0; - u64 codec_bfs, cpu_bfs; - - dbg("asoc: match version %s\n", SND_SOC_VERSION); - clk_info.rate = rate; - pcmrate = soc_get_rate_format(rate); - - /* try and find a match from the codec and cpu DAI capabilities */ - for (j = 0; j < rtd->codec_dai->caps.num_modes; j++) { - for (k = 0; k < rtd->cpu_dai->caps.num_modes; k++) { - codec_dai_mode = &rtd->codec_dai->caps.mode[j]; - cpu_dai_mode = &rtd->cpu_dai->caps.mode[k]; - - if (!(codec_dai_mode->pcmrate & cpu_dai_mode->pcmrate & - pcmrate)) { - dbgc("asoc: DAI[%d:%d] failed to match rate\n", j, k); - continue; - } - - fmt = codec_dai_mode->fmt & cpu_dai_mode->fmt; - if (!(fmt & SND_SOC_DAIFMT_FORMAT_MASK)) { - dbgc("asoc: DAI[%d:%d] failed to match format\n", j, k); - continue; - } - - if (!(fmt & SND_SOC_DAIFMT_CLOCK_MASK)) { - dbgc("asoc: DAI[%d:%d] failed to match clock masters\n", - j, k); - continue; - } - - if (!(fmt & SND_SOC_DAIFMT_INV_MASK)) { - dbgc("asoc: DAI[%d:%d] failed to match invert\n", j, k); - continue; - } - - if (!(codec_dai_mode->pcmfmt & cpu_dai_mode->pcmfmt)) { - dbgc("asoc: DAI[%d:%d] failed to match pcm format\n", j, k); - continue; - } - - if (!(codec_dai_mode->pcmdir & cpu_dai_mode->pcmdir)) { - dbgc("asoc: DAI[%d:%d] failed to match direction\n", j, k); - continue; - } - - /* todo - still need to add tdm selection */ - rtd->cpu_dai->dai_runtime.fmt = - rtd->codec_dai->dai_runtime.fmt = - 1 << (ffs(fmt & SND_SOC_DAIFMT_FORMAT_MASK) -1) | - 1 << (ffs(fmt & SND_SOC_DAIFMT_CLOCK_MASK) - 1) | - 1 << (ffs(fmt & SND_SOC_DAIFMT_INV_MASK) - 1); - clk_info.bclk_master = - rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK; - - /* make sure the ratio between rate and master - * clock is acceptable*/ - fs = (cpu_dai_mode->fs & codec_dai_mode->fs); - if (fs == 0) { - dbgc("asoc: DAI[%d:%d] failed to match FS\n", j, k); - continue; - } - clk_info.fs = rtd->cpu_dai->dai_runtime.fs = - rtd->codec_dai->dai_runtime.fs = fs; - - /* calculate audio system clocking using slowest clocks possible*/ - mclk = soc_get_mclk(rtd, &clk_info); - if (mclk == 0) { - dbgc("asoc: DAI[%d:%d] configuration not clockable\n", j, k); - dbgc("asoc: rate %d fs %d master %x\n", rate, fs, - clk_info.bclk_master); - continue; - } - - /* calculate word size (per channel) and frame size */ - rtd->codec_dai->dai_runtime.pcmfmt = - rtd->cpu_dai->dai_runtime.pcmfmt = - 1 << params_format(params); - - chn = params_channels(params); - /* i2s always has left and right */ - if (params_channels(params) == 1 && - rtd->cpu_dai->dai_runtime.fmt & (SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_LEFT_J)) - chn <<= 1; - - /* Calculate bfs - the ratio between bitclock and the sample rate - * We must take into consideration the dividers and multipliers - * used in the codec and cpu DAI modes. We always choose the - * lowest possible clocks to reduce power. - */ - switch (CODEC_CPU(codec_dai_mode->flags, cpu_dai_mode->flags)) { - case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_DIV): - /* cpu & codec bfs dividers */ - rtd->cpu_dai->dai_runtime.bfs = - rtd->codec_dai->dai_runtime.bfs = - 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1); - break; - case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RCW): - /* normalise bfs codec divider & cpu rcw mult */ - codec_bfs = soc_bfs_div_to_rcw(codec_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - rtd->cpu_dai->dai_runtime.bfs = - 1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1); - cpu_bfs = soc_bfs_rcw_to_div(cpu_dai_mode->bfs, rate, mclk, - rtd->codec_dai->dai_runtime.pcmfmt, chn); - rtd->codec_dai->dai_runtime.bfs = - 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1); - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_DIV): - /* normalise bfs codec rcw mult & cpu divider */ - codec_bfs = soc_bfs_rcw_to_div(codec_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - rtd->cpu_dai->dai_runtime.bfs = - 1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1); - cpu_bfs = soc_bfs_div_to_rcw(cpu_dai_mode->bfs, rate, mclk, - rtd->codec_dai->dai_runtime.pcmfmt, chn); - rtd->codec_dai->dai_runtime.bfs = - 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1); - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RCW): - /* codec & cpu bfs rate rcw multipliers */ - rtd->cpu_dai->dai_runtime.bfs = - rtd->codec_dai->dai_runtime.bfs = - 1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1); - break; - case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RATE): - /* normalise cpu bfs rate const multiplier & codec div */ - cpu_bfs = soc_bfs_rate_to_div(cpu_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - if(codec_dai_mode->bfs & cpu_bfs) { - rtd->codec_dai->dai_runtime.bfs = cpu_bfs; - rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs; - } else - rtd->cpu_dai->dai_runtime.bfs = 0; - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RATE): - /* normalise cpu bfs rate const multiplier & codec rcw mult */ - cpu_bfs = soc_bfs_rate_to_rcw(cpu_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - if(codec_dai_mode->bfs & cpu_bfs) { - rtd->codec_dai->dai_runtime.bfs = cpu_bfs; - rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs; - } else - rtd->cpu_dai->dai_runtime.bfs = 0; - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RCW): - /* normalise cpu bfs rate rcw multiplier & codec const mult */ - codec_bfs = soc_bfs_rate_to_rcw(codec_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - if(cpu_dai_mode->bfs & codec_bfs) { - rtd->cpu_dai->dai_runtime.bfs = codec_bfs; - rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs; - } else - rtd->cpu_dai->dai_runtime.bfs = 0; - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_DIV): - /* normalise cpu bfs div & codec const mult */ - codec_bfs = soc_bfs_rate_to_div(codec_dai_mode->bfs, rate, - mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn); - if(cpu_dai_mode->bfs & codec_bfs) { - rtd->cpu_dai->dai_runtime.bfs = codec_bfs; - rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs; - } else - rtd->cpu_dai->dai_runtime.bfs = 0; - break; - case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RATE): - /* cpu & codec constant mult */ - if(codec_dai_mode->bfs == cpu_dai_mode->bfs) - rtd->cpu_dai->dai_runtime.bfs = - rtd->codec_dai->dai_runtime.bfs = - codec_dai_mode->bfs; - else - rtd->cpu_dai->dai_runtime.bfs = - rtd->codec_dai->dai_runtime.bfs = 0; - break; - } - - /* make sure the bit clock speed is acceptable */ - if (!rtd->cpu_dai->dai_runtime.bfs || - !rtd->codec_dai->dai_runtime.bfs) { - dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k); - dbgc("asoc: cpu_dai %llu codec %llu\n", - rtd->cpu_dai->dai_runtime.bfs, - rtd->codec_dai->dai_runtime.bfs); - dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt); - continue; - } - - goto found; - } - } - printk(KERN_ERR "asoc: no matching DAI found between codec and CPU\n"); - return -EINVAL; - -found: - /* we have matching DAI's, so complete the runtime info */ - rtd->codec_dai->dai_runtime.pcmrate = - rtd->cpu_dai->dai_runtime.pcmrate = - soc_get_rate_format(rate); - - rtd->codec_dai->dai_runtime.priv = codec_dai_mode->priv; - rtd->cpu_dai->dai_runtime.priv = cpu_dai_mode->priv; - rtd->codec_dai->dai_runtime.flags = codec_dai_mode->flags; - rtd->cpu_dai->dai_runtime.flags = cpu_dai_mode->flags; - - /* for debug atm */ - dbg("asoc: DAI[%d:%d] Match OK\n", j, k); - if (rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) { - codec_bclk = (rtd->codec_dai->dai_runtime.fs * params_rate(params)) / - SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs); - dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n", - rtd->codec_dai->dai_runtime.fs, mclk, - SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); - } else if(rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) { - codec_bclk = params_rate(params) * rtd->codec_dai->dai_runtime.bfs; - dbg("asoc: codec fs %d mclk %d bfs rate mult %llu bclk %d\n", - rtd->codec_dai->dai_runtime.fs, mclk, - rtd->codec_dai->dai_runtime.bfs, codec_bclk); - } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) { - codec_bclk = params_rate(params) * params_channels(params) * - snd_pcm_format_physical_width(rtd->codec_dai->dai_runtime.pcmfmt) * - SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs); - dbg("asoc: codec fs %d mclk %d bfs rcw mult %d bclk %d\n", - rtd->codec_dai->dai_runtime.fs, mclk, - SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk); - } else - codec_bclk = 0; - - if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) { - cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) / - SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs); - dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n", - rtd->cpu_dai->dai_runtime.fs, mclk, - SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); - } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) { - cpu_bclk = params_rate(params) * rtd->cpu_dai->dai_runtime.bfs; - dbg("asoc: cpu fs %d mclk %d bfs rate mult %llu bclk %d\n", - rtd->cpu_dai->dai_runtime.fs, mclk, - rtd->cpu_dai->dai_runtime.bfs, cpu_bclk); - } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) { - cpu_bclk = params_rate(params) * params_channels(params) * - snd_pcm_format_physical_width(rtd->cpu_dai->dai_runtime.pcmfmt) * - SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs); - dbg("asoc: cpu fs %d mclk %d bfs mult rcw %d bclk %d\n", - rtd->cpu_dai->dai_runtime.fs, mclk, - SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk); - } else - cpu_bclk = 0; - - /* - * Check we have matching bitclocks. If we don't then it means the - * sysclock returned by either the codec or cpu DAI (selected by the - * machine sysclock function) is wrong compared with the supported DAI - * modes for the codec or cpu DAI. Check your codec or CPU DAI - * config_sysclock() functions. - */ - if (cpu_bclk != codec_bclk && cpu_bclk){ - printk(KERN_ERR - "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n" - ); - printk(KERN_ERR "asoc: codec %d != cpu %d\n", codec_bclk, cpu_bclk); - } - - switch(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - dbg("asoc: DAI codec BCLK master, LRC master\n"); - break; - case SND_SOC_DAIFMT_CBS_CFM: - dbg("asoc: DAI codec BCLK slave, LRC master\n"); - break; - case SND_SOC_DAIFMT_CBM_CFS: - dbg("asoc: DAI codec BCLK master, LRC slave\n"); - break; - case SND_SOC_DAIFMT_CBS_CFS: - dbg("asoc: DAI codec BCLK slave, LRC slave\n"); - break; - } - dbg("asoc: mode %x, invert %x\n", - rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK, - rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK); - dbg("asoc: audio rate %d chn %d fmt %x\n", params_rate(params), - params_channels(params), params_format(params)); - - return 0; -} - -static inline u32 get_rates(struct snd_soc_dai_mode *modes, int nmodes) -{ - int i; - u32 rates = 0; - - for(i = 0; i < nmodes; i++) - rates |= modes[i].pcmrate; - - return rates; -} - -static inline u64 get_formats(struct snd_soc_dai_mode *modes, int nmodes) -{ - int i; - u64 formats = 0; - - for(i = 0; i < nmodes; i++) - formats |= modes[i].pcmfmt; - - return formats; -} - /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -604,20 +136,20 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_codec_dai *codec_dai = rtd->codec_dai; - struct snd_soc_cpu_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; int ret = 0; mutex_lock(&pcm_mutex); /* startup the audio subsystem */ - if (rtd->cpu_dai->ops.startup) { - ret = rtd->cpu_dai->ops.startup(substream); + if (cpu_dai->ops.startup) { + ret = cpu_dai->ops.startup(substream); if (ret < 0) { printk(KERN_ERR "asoc: can't open interface %s\n", - rtd->cpu_dai->name); + cpu_dai->name); goto out; } } @@ -630,116 +162,101 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (machine->ops && machine->ops->startup) { - ret = machine->ops->startup(substream); + if (codec_dai->ops.startup) { + ret = codec_dai->ops.startup(substream); if (ret < 0) { - printk(KERN_ERR "asoc: %s startup failed\n", machine->name); - goto machine_err; + printk(KERN_ERR "asoc: can't open codec %s\n", + codec_dai->name); + goto codec_dai_err; } } - if (rtd->codec_dai->ops.startup) { - ret = rtd->codec_dai->ops.startup(substream); + if (machine->ops && machine->ops->startup) { + ret = machine->ops->startup(substream); if (ret < 0) { - printk(KERN_ERR "asoc: can't open codec %s\n", - rtd->codec_dai->name); - goto codec_dai_err; + printk(KERN_ERR "asoc: %s startup failed\n", machine->name); + goto machine_err; } } - /* create runtime params from DMA, codec and cpu DAI */ - if (runtime->hw.rates) - runtime->hw.rates &= - get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) & - get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes); - else - runtime->hw.rates = - get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) & - get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes); - if (runtime->hw.formats) - runtime->hw.formats &= - get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) & - get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes); - else - runtime->hw.formats = - get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) & - get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes); - /* Check that the codec and cpu DAI's are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = - max(rtd->codec_dai->playback.rate_min, - rtd->cpu_dai->playback.rate_min); + max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min); runtime->hw.rate_max = - min(rtd->codec_dai->playback.rate_max, - rtd->cpu_dai->playback.rate_max); + min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max); runtime->hw.channels_min = - max(rtd->codec_dai->playback.channels_min, - rtd->cpu_dai->playback.channels_min); + max(codec_dai->playback.channels_min, + cpu_dai->playback.channels_min); runtime->hw.channels_max = - min(rtd->codec_dai->playback.channels_max, - rtd->cpu_dai->playback.channels_max); + min(codec_dai->playback.channels_max, + cpu_dai->playback.channels_max); + runtime->hw.formats = + codec_dai->playback.formats & cpu_dai->playback.formats; + runtime->hw.rates = + codec_dai->playback.rates & cpu_dai->playback.rates; } else { runtime->hw.rate_min = - max(rtd->codec_dai->capture.rate_min, - rtd->cpu_dai->capture.rate_min); + max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min); runtime->hw.rate_max = - min(rtd->codec_dai->capture.rate_max, - rtd->cpu_dai->capture.rate_max); + min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max); runtime->hw.channels_min = - max(rtd->codec_dai->capture.channels_min, - rtd->cpu_dai->capture.channels_min); + max(codec_dai->capture.channels_min, + cpu_dai->capture.channels_min); runtime->hw.channels_max = - min(rtd->codec_dai->capture.channels_max, - rtd->cpu_dai->capture.channels_max); + min(codec_dai->capture.channels_max, + cpu_dai->capture.channels_max); + runtime->hw.formats = + codec_dai->capture.formats & cpu_dai->capture.formats; + runtime->hw.rates = + codec_dai->capture.rates & cpu_dai->capture.rates; } snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", - rtd->codec_dai->name, rtd->cpu_dai->name); - goto codec_dai_err; + codec_dai->name, cpu_dai->name); + goto machine_err; } if (!runtime->hw.formats) { printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", - rtd->codec_dai->name, rtd->cpu_dai->name); - goto codec_dai_err; + codec_dai->name, cpu_dai->name); + goto machine_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max) { printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", - rtd->codec_dai->name, rtd->cpu_dai->name); - goto codec_dai_err; + codec_dai->name, cpu_dai->name); + goto machine_err; } - dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name); + dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name); dbg("asoc: rate mask 0x%x\n", runtime->hw.rates); dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, runtime->hw.channels_max); dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 1; + cpu_dai->playback.active = codec_dai->playback.active = 1; else - rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 1; - rtd->cpu_dai->active = rtd->codec_dai->active = 1; - rtd->cpu_dai->runtime = runtime; + cpu_dai->capture.active = codec_dai->capture.active = 1; + cpu_dai->active = codec_dai->active = 1; + cpu_dai->runtime = runtime; socdev->codec->active++; mutex_unlock(&pcm_mutex); return 0; -codec_dai_err: +machine_err: if (machine->ops && machine->ops->shutdown) machine->ops->shutdown(substream); -machine_err: +codec_dai_err: if (platform->pcm_ops->close) platform->pcm_ops->close(substream); platform_err: - if (rtd->cpu_dai->ops.shutdown) - rtd->cpu_dai->ops.shutdown(substream); + if (cpu_dai->ops.shutdown) + cpu_dai->ops.shutdown(substream); out: mutex_unlock(&pcm_mutex); return ret; @@ -795,47 +312,49 @@ static int soc_codec_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; mutex_lock(&pcm_mutex); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 0; + cpu_dai->playback.active = codec_dai->playback.active = 0; else - rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 0; + cpu_dai->capture.active = codec_dai->capture.active = 0; - if (rtd->codec_dai->playback.active == 0 && - rtd->codec_dai->capture.active == 0) { - rtd->cpu_dai->active = rtd->codec_dai->active = 0; + if (codec_dai->playback.active == 0 && + codec_dai->capture.active == 0) { + cpu_dai->active = codec_dai->active = 0; } codec->active--; - if (rtd->cpu_dai->ops.shutdown) - rtd->cpu_dai->ops.shutdown(substream); + if (cpu_dai->ops.shutdown) + cpu_dai->ops.shutdown(substream); - if (rtd->codec_dai->ops.shutdown) - rtd->codec_dai->ops.shutdown(substream); + if (codec_dai->ops.shutdown) + codec_dai->ops.shutdown(substream); if (machine->ops && machine->ops->shutdown) machine->ops->shutdown(substream); if (platform->pcm_ops->close) platform->pcm_ops->close(substream); - rtd->cpu_dai->runtime = NULL; + cpu_dai->runtime = NULL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* start delayed pop wq here for playback streams */ - rtd->codec_dai->pop_wait = 1; + codec_dai->pop_wait = 1; schedule_delayed_work(&socdev->delayed_work, msecs_to_jiffies(pmdown_time)); } else { /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(codec, rtd->codec_dai->capture.stream_name, - SND_SOC_DAPM_STREAM_STOP); + snd_soc_dapm_stream_event(codec, + codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP); - if (codec->active == 0 && rtd->codec_dai->pop_wait == 0){ + if (codec->active == 0 && codec_dai->pop_wait == 0){ if (codec->dapm_event) codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); } @@ -854,11 +373,23 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; int ret = 0; mutex_lock(&pcm_mutex); + + if (machine->ops && machine->ops->prepare) { + ret = machine->ops->prepare(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: machine prepare error\n"); + goto out; + } + } + if (platform->pcm_ops->prepare) { ret = platform->pcm_ops->prepare(substream); if (ret < 0) { @@ -867,30 +398,35 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (rtd->codec_dai->ops.prepare) { - ret = rtd->codec_dai->ops.prepare(substream); + if (codec_dai->ops.prepare) { + ret = codec_dai->ops.prepare(substream); if (ret < 0) { printk(KERN_ERR "asoc: codec DAI prepare error\n"); goto out; } } - if (rtd->cpu_dai->ops.prepare) - ret = rtd->cpu_dai->ops.prepare(substream); + if (cpu_dai->ops.prepare) { + ret = cpu_dai->ops.prepare(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: cpu DAI prepare error\n"); + goto out; + } + } /* we only want to start a DAPM playback stream if we are not waiting * on an existing one stopping */ - if (rtd->codec_dai->pop_wait) { + if (codec_dai->pop_wait) { /* we are waiting for the delayed work to start */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - snd_soc_dapm_stream_event(codec, - rtd->codec_dai->capture.stream_name, + snd_soc_dapm_stream_event(socdev->codec, + codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); else { - rtd->codec_dai->pop_wait = 0; + codec_dai->pop_wait = 0; cancel_delayed_work(&socdev->delayed_work); - if (rtd->codec_dai->digital_mute) - rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + if (codec_dai->dai_ops.digital_mute) + codec_dai->dai_ops.digital_mute(codec_dai, 0); } } else { /* no delayed work - do we need to power up codec */ @@ -901,30 +437,30 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec, - rtd->codec_dai->playback.stream_name, + codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_START); else snd_soc_dapm_stream_event(codec, - rtd->codec_dai->capture.stream_name, + codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); if (codec->dapm_event) codec->dapm_event(codec, SNDRV_CTL_POWER_D0); - if (rtd->codec_dai->digital_mute) - rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + if (codec_dai->dai_ops.digital_mute) + codec_dai->dai_ops.digital_mute(codec_dai, 0); } else { /* codec already powered - power on widgets */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec, - rtd->codec_dai->playback.stream_name, + codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_START); else snd_soc_dapm_stream_event(codec, - rtd->codec_dai->capture.stream_name, + codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); - if (rtd->codec_dai->digital_mute) - rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0); + if (codec_dai->dai_ops.digital_mute) + codec_dai->dai_ops.digital_mute(codec_dai, 0); } } @@ -943,39 +479,36 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; int ret = 0; mutex_lock(&pcm_mutex); - /* we don't need to match any AC97 params */ - if (rtd->cpu_dai->type != SND_SOC_DAI_AC97) { - ret = soc_hw_match_params(substream, params); - if (ret < 0) - goto out; - } else { - struct snd_soc_clock_info clk_info; - clk_info.rate = params_rate(params); - ret = soc_get_mclk(rtd, &clk_info); - if (ret < 0) + if (machine->ops && machine->ops->hw_params) { + ret = machine->ops->hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: machine hw_params failed\n"); goto out; + } } - if (rtd->codec_dai->ops.hw_params) { - ret = rtd->codec_dai->ops.hw_params(substream, params); + if (codec_dai->ops.hw_params) { + ret = codec_dai->ops.hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: can't set codec %s hw params\n", - rtd->codec_dai->name); - goto out; + codec_dai->name); + goto codec_err; } } - if (rtd->cpu_dai->ops.hw_params) { - ret = rtd->cpu_dai->ops.hw_params(substream, params); + if (cpu_dai->ops.hw_params) { + ret = cpu_dai->ops.hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: can't set interface %s hw params\n", - rtd->cpu_dai->name); + cpu_dai->name); goto interface_err; } } @@ -989,29 +522,21 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } - if (machine->ops && machine->ops->hw_params) { - ret = machine->ops->hw_params(substream, params); - if (ret < 0) { - printk(KERN_ERR "asoc: machine hw_params failed\n"); - goto machine_err; - } - } - out: mutex_unlock(&pcm_mutex); return ret; -machine_err: - if (platform->pcm_ops->hw_free) - platform->pcm_ops->hw_free(substream); - platform_err: - if (rtd->cpu_dai->ops.hw_free) - rtd->cpu_dai->ops.hw_free(substream); + if (cpu_dai->ops.hw_free) + cpu_dai->ops.hw_free(substream); interface_err: - if (rtd->codec_dai->ops.hw_free) - rtd->codec_dai->ops.hw_free(substream); + if (codec_dai->ops.hw_free) + codec_dai->ops.hw_free(substream); + +codec_err: + if(machine->ops && machine->ops->hw_free) + machine->ops->hw_free(substream); mutex_unlock(&pcm_mutex); return ret; @@ -1024,15 +549,17 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_machine *machine = socdev->machine; mutex_lock(&pcm_mutex); /* apply codec digital mute */ - if (!codec->active && rtd->codec_dai->digital_mute) - rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 1); + if (!codec->active && codec_dai->dai_ops.digital_mute) + codec_dai->dai_ops.digital_mute(codec_dai, 1); /* free any machine hw params */ if (machine->ops && machine->ops->hw_free) @@ -1043,11 +570,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) platform->pcm_ops->hw_free(substream); /* now free hw params for the DAI's */ - if (rtd->codec_dai->ops.hw_free) - rtd->codec_dai->ops.hw_free(substream); + if (codec_dai->ops.hw_free) + codec_dai->ops.hw_free(substream); - if (rtd->cpu_dai->ops.hw_free) - rtd->cpu_dai->ops.hw_free(substream); + if (cpu_dai->ops.hw_free) + cpu_dai->ops.hw_free(substream); mutex_unlock(&pcm_mutex); return 0; @@ -1057,11 +584,14 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; + struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_codec_dai *codec_dai = machine->codec_dai; int ret; - if (rtd->codec_dai->ops.trigger) { - ret = rtd->codec_dai->ops.trigger(substream, cmd); + if (codec_dai->ops.trigger) { + ret = codec_dai->ops.trigger(substream, cmd); if (ret < 0) return ret; } @@ -1072,8 +602,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } - if (rtd->cpu_dai->ops.trigger) { - ret = rtd->cpu_dai->ops.trigger(substream, cmd); + if (cpu_dai->ops.trigger) { + ret = cpu_dai->ops.trigger(substream, cmd); if (ret < 0) return ret; } @@ -1104,8 +634,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) /* mute any active DAC's */ for(i = 0; i < machine->num_links; i++) { struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; - if (dai->digital_mute && dai->playback.active) - dai->digital_mute(codec, dai, 1); + if (dai->dai_ops.digital_mute && dai->playback.active) + dai->dai_ops.digital_mute(dai, 1); } if (machine->suspend_pre) @@ -1185,8 +715,8 @@ static int soc_resume(struct platform_device *pdev) /* unmute any active DAC's */ for(i = 0; i < machine->num_links; i++) { struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; - if (dai->digital_mute && dai->playback.active) - dai->digital_mute(codec, dai, 0); + if (dai->dai_ops.digital_mute && dai->playback.active) + dai->dai_ops.digital_mute(dai, 0); } for(i = 0; i < machine->num_links; i++) { @@ -1320,9 +850,10 @@ static int soc_new_pcm(struct snd_soc_device *socdev, rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); if (rtd == NULL) return -ENOMEM; - rtd->cpu_dai = cpu_dai; - rtd->codec_dai = codec_dai; + + rtd->dai = dai_link; rtd->socdev = socdev; + codec_dai->codec = socdev->codec; /* check client and interface hw capabilities */ sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name, @@ -1498,22 +1029,6 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, } EXPORT_SYMBOL_GPL(snd_soc_test_bits); -/** - * snd_soc_get_rate - get int sample rate - * @hwpcmrate: the hardware pcm rate - * - * Returns the audio rate integaer value, else 0. - */ -int snd_soc_get_rate(int hwpcmrate) -{ - int rate = ffs(hwpcmrate) - 1; - - if (rate > ARRAY_SIZE(rates)) - return 0; - return rates[rate]; -} -EXPORT_SYMBOL_GPL(snd_soc_get_rate); - /** * snd_soc_new_pcms - create new sound card and pcms * @socdev: the SoC audio device -- cgit v1.2.3-59-g8ed1b From 11da21a79048472a14b201120c0c50b10060220b Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 2 Feb 2007 17:14:19 +0100 Subject: [ALSA] soc - 0.13 ASoC DAPM bug fix for unnamed streams This patch fixes a bug whereby an unnamed stream would cause a NULL pointer ref in snd_soc_dapm_stream_event(). Signed-off-by: Seth Forshee Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/soc-dapm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5c2a34956a5d..d0162a4cb7fd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1239,6 +1239,9 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, { struct snd_soc_dapm_widget *w; + if (stream == NULL) + return 0; + mutex_lock(&codec->mutex); list_for_each_entry(w, &codec->dapm_widgets, list) { -- cgit v1.2.3-59-g8ed1b From b36d61d45654104c04ff71055ef09c696fea5f89 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 2 Feb 2007 17:14:56 +0100 Subject: [ALSA] soc - ASoC 0.13 WM8731 codec This patch updates the WM8731 codec driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8731.c | 379 +++++++++++++++------------------------------- sound/soc/codecs/wm8731.h | 3 + 2 files changed, 127 insertions(+), 255 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 82f440fcf294..e6b990507df2 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -30,7 +30,7 @@ #include "wm8731.h" #define AUDIO_NAME "wm8731" -#define WM8731_VERSION "0.12" +#define WM8731_VERSION "0.13" /* * Debug @@ -53,6 +53,11 @@ struct snd_soc_codec_device soc_codec_dev_wm8731; +/* codec private data */ +struct wm8731_priv { + unsigned int sysclk; +}; + /* * wm8731 register cache * We can't read the WM8731 register space when we are @@ -65,191 +70,6 @@ static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { 0x0000, 0x0000 }; -#define WM8731_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ - SND_SOC_DAIFMT_IB_IF) - -#define WM8731_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define WM8731_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - -#define WM8731_HIFI_BITS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) - -static struct snd_soc_dai_mode wm8731_modes[] = { - /* codec frame and clock master modes */ - /* 8k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 1536, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 2304, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 1408, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 2112, - .bfs = 64, - }, - - /* 32k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 384, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 576, - .bfs = 64, - }, - - /* 44.1k & 48k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 256, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 384, - .bfs = 64, - }, - - /* 88.2 & 96k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 128, - .bfs = 64, - }, - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 192, - .bfs = 64, - }, - - /* USB codec frame and clock master modes */ - /* 8k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, - .bfs = SND_SOC_FSBD(1), - }, - - /* 44.1k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 272, - .bfs = SND_SOC_FSBD(1), - }, - - /* 48k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, - .bfs = SND_SOC_FSBD(1), - }, - - /* 88.2k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 136, - .bfs = SND_SOC_FSBD(1), - }, - - /* 96k */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_96000, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 125, - .bfs = SND_SOC_FSBD(1), - }, - - /* codec frame and clock slave modes */ - { - .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = WM8731_HIFI_BITS, - .pcmrate = WM8731_RATES, - .pcmdir = WM8731_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB_ALL, - }, -}; - /* * read wm8731 register cache */ @@ -471,18 +291,34 @@ static inline int get_coeff(int mclk, int rate) return 0; } -/* WM8731 supports numerous clocks per sample rate */ -static unsigned int wm8731_config_sysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) +static int wm8731_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - dai->mclk = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + struct wm8731_priv *wm8731 = codec->private_data; + u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3; + int i = get_coeff(wm8731->sysclk, params_rate(params)); + u16 srate = (coeff_div[i].sr << 2) | + (coeff_div[i].bosr << 1) | coeff_div[i].usb; - /* check that the calculated FS and rate actually match a clock from - * the machine driver */ - if (info->fs * info->rate == clk) - dai->mclk = clk; + wm8731_write(codec, WM8731_SRATE, srate); + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + } - return dai->mclk; + wm8731_write(codec, WM8731_IFACE, iface); + return 0; } static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) @@ -490,24 +326,76 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->codec; - u16 iface = 0, srate; - int i = get_coeff(rtd->codec_dai->mclk, - snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate)); + + /* set active */ + wm8731_write(codec, WM8731_ACTIVE, 0x0001); + + return 0; +} + +static void wm8731_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + + /* deactivate */ + if (!codec->active) { + udelay(50); + wm8731_write(codec, WM8731_ACTIVE, 0x0); + } +} + +static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7; + + if (mute) + wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8); + else + wm8731_write(codec, WM8731_APDIGI, mute_reg); + return 0; +} + +static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8731_priv *wm8731 = codec->private_data; + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8731->sysclk = freq; + return 0; + } + return -EINVAL; +} + + +static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; /* set master/slave audio interface */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: iface |= 0x0040; break; case SND_SOC_DAIFMT_CBS_CFS: break; + default: + return -EINVAL; } - srate = (coeff_div[i].sr << 2) | - (coeff_div[i].bosr << 1) | coeff_div[i].usb; - wm8731_write(codec, WM8731_SRATE, srate); /* interface format */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x0002; break; @@ -522,25 +410,12 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) case SND_SOC_DAIFMT_DSP_B: iface |= 0x0013; break; - } - - /* bit size */ - switch (rtd->codec_dai->dai_runtime.pcmfmt) { - case SNDRV_PCM_FMTBIT_S16_LE: - break; - case SNDRV_PCM_FMTBIT_S20_3LE: - iface |= 0x0004; - break; - case SNDRV_PCM_FMTBIT_S24_LE: - iface |= 0x0008; - break; - case SNDRV_PCM_FMTBIT_S32_LE: - iface |= 0x000c; - break; + default: + return -EINVAL; } /* clock inversion */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) { + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: @@ -552,37 +427,12 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) case SND_SOC_DAIFMT_NB_IF: iface |= 0x0010; break; + default: + return -EINVAL; } /* set iface */ wm8731_write(codec, WM8731_IFACE, iface); - - /* set active */ - wm8731_write(codec, WM8731_ACTIVE, 0x0001); - return 0; -} - -static void wm8731_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; - - /* deactivate */ - if (!codec->active) { - udelay(50); - wm8731_write(codec, WM8731_ACTIVE, 0x0); - } -} - -static int wm8731_mute(struct snd_soc_codec *codec, - struct snd_soc_codec_dai *dai, int mute) -{ - u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7; - if (mute) - wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8); - else - wm8731_write(codec, WM8731_APDIGI, mute_reg); return 0; } @@ -612,28 +462,39 @@ static int wm8731_dapm_event(struct snd_soc_codec *codec, int event) return 0; } +#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) + +#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + struct snd_soc_codec_dai wm8731_dai = { .name = "WM8731", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - }, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, - }, - .config_sysclk = wm8731_config_sysclk, - .digital_mute = wm8731_mute, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, .ops = { .prepare = wm8731_pcm_prepare, + .hw_params = wm8731_hw_params, .shutdown = wm8731_shutdown, }, - .caps = { - .num_modes = ARRAY_SIZE(wm8731_modes), - .mode = wm8731_modes, - }, + .dai_ops = { + .digital_mute = wm8731_mute, + .set_sysclk = wm8731_set_dai_sysclk, + .set_fmt = wm8731_set_dai_fmt, + } }; EXPORT_SYMBOL_GPL(wm8731_dai); @@ -683,7 +544,6 @@ static int wm8731_init(struct snd_soc_device *socdev) codec->dai = &wm8731_dai; codec->num_dai = 1; codec->reg_cache_size = ARRAY_SIZE(wm8731_reg); - codec->reg_cache = kzalloc(sizeof(u16) * ARRAY_SIZE(wm8731_reg), GFP_KERNEL); if (codec->reg_cache == NULL) @@ -832,6 +692,7 @@ static int wm8731_probe(struct platform_device *pdev) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct wm8731_setup_data *setup; struct snd_soc_codec *codec; + struct wm8731_priv *wm8731; int ret = 0; info("WM8731 Audio Codec %s", WM8731_VERSION); @@ -841,6 +702,13 @@ static int wm8731_probe(struct platform_device *pdev) if (codec == NULL) return -ENOMEM; + wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); + if (wm8731 == NULL) { + kfree(codec); + return -ENOMEM; + } + + codec->private_data = wm8731; socdev->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); @@ -875,6 +743,7 @@ static int wm8731_remove(struct platform_device *pdev) #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) i2c_del_driver(&wm8731_i2c_driver); #endif + kfree(codec->private_data); kfree(codec); return 0; diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h index 8fa0f53bef1c..5bcab6a7afb4 100644 --- a/sound/soc/codecs/wm8731.h +++ b/sound/soc/codecs/wm8731.h @@ -31,6 +31,9 @@ #define WM8731_CACHEREGNUM 10 +#define WM8731_SYSCLK 0 +#define WM8731_DAI 0 + struct wm8731_setup_data { unsigned short i2c_address; }; -- cgit v1.2.3-59-g8ed1b From 4422b606bc04eab01dd5cb6f8e6dd0608d65bb11 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:15:33 +0100 Subject: [ALSA] soc - ASoC 0.13 WM8750 codec driver This patch updates the WM8750 codec driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8750.c | 446 ++++++++++++---------------------------------- sound/soc/codecs/wm8750.h | 3 +- 2 files changed, 113 insertions(+), 336 deletions(-) diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index d4a288ba644b..c1ffb61ef7f7 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -30,7 +30,7 @@ #include "wm8750.h" #define AUDIO_NAME "WM8750" -#define WM8750_VERSION "0.11" +#define WM8750_VERSION "0.12" /* * Debug @@ -51,6 +51,11 @@ #define warn(format, arg...) \ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) +/* codec private data */ +struct wm8750_priv { + unsigned int sysclk; +}; + /* * wm8750 register cache * We can't read the WM8750 register space when we @@ -70,280 +75,6 @@ static const u16 wm8750_reg[] = { 0x0079, 0x0079, 0x0079, /* 40 */ }; -#define WM8750_HIFI_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ - SND_SOC_DAIFMT_IB_IF) - -#define WM8750_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define WM8750_HIFI_FSB \ - (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ - SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) - -#define WM8750_HIFI_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - -#define WM8750_HIFI_BITS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) - -static struct snd_soc_dai_mode wm8750_modes[] = { - /* common codec frame and clock master modes */ - /* 8k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1536, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1408, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 2304, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 2112, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, - .bfs = WM8750_HIFI_FSB, - }, - - /* 11.025k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1024, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1536, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1088, - .bfs = WM8750_HIFI_FSB, - }, - - /* 16k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 768, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1152, - .bfs = WM8750_HIFI_FSB - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 750, - .bfs = WM8750_HIFI_FSB, - }, - - /* 22.05k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 512, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 768, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 544, - .bfs = WM8750_HIFI_FSB, - }, - - /* 32k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 384, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 576, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 375, - .bfs = WM8750_HIFI_FSB, - }, - - /* 44.1k & 48k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 384, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 272, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, - .bfs = WM8750_HIFI_FSB, - }, - - /* 88.2k & 96k */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 128, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 192, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_88200, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 136, - .bfs = WM8750_HIFI_FSB, - }, - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = SNDRV_PCM_RATE_96000, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 125, - .bfs = WM8750_HIFI_FSB, - }, - - /* codec frame and clock slave modes */ - { - .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = WM8750_HIFI_BITS, - .pcmrate = WM8750_HIFI_RATES, - .pcmdir = WM8750_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB_ALL, - }, -}; - /* * read wm8750 register cache */ @@ -834,40 +565,43 @@ static inline int get_coeff(int mclk, int rate) return -EINVAL; } -/* WM8750 supports numerous input clocks per sample rate */ -static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) +static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, + int clk_id, unsigned int freq, int dir) { - dai->mclk = clk; - return dai->mclk; + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8750_priv *wm8750 = codec->private_data; + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8750->sysclk = freq; + return 0; + } + return -EINVAL; } -static int wm8750_pcm_prepare(struct snd_pcm_substream *substream) +static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; - u16 iface = 0, bfs, srate = 0; - int i = get_coeff(rtd->codec_dai->mclk, - snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate)); - - /* is coefficient valid ? */ - if (i < 0) - return i; - - bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs); + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; /* set master/slave audio interface */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: iface = 0x0040; break; case SND_SOC_DAIFMT_CBS_CFS: break; + default: + return -EINVAL; } /* interface format */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x0002; break; @@ -882,25 +616,12 @@ static int wm8750_pcm_prepare(struct snd_pcm_substream *substream) case SND_SOC_DAIFMT_DSP_B: iface |= 0x0013; break; - } - - /* bit size */ - switch (rtd->codec_dai->dai_runtime.pcmfmt) { - case SNDRV_PCM_FMTBIT_S16_LE: - break; - case SNDRV_PCM_FMTBIT_S20_3LE: - iface |= 0x0004; - break; - case SNDRV_PCM_FMTBIT_S24_LE: - iface |= 0x0008; - break; - case SNDRV_PCM_FMTBIT_S32_LE: - iface |= 0x000c; - break; + default: + return -EINVAL; } /* clock inversion */ - switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) { + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: @@ -912,35 +633,54 @@ static int wm8750_pcm_prepare(struct snd_pcm_substream *substream) case SND_SOC_DAIFMT_NB_IF: iface |= 0x0010; break; + default: + return -EINVAL; } - /* set bclk divisor rate */ - switch (bfs) { - case 1: + wm8750_write(codec, WM8750_IFACE, iface); + return 0; +} + +static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + struct wm8750_priv *wm8750 = codec->private_data; + u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3; + u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0; + int coeff = get_coeff(wm8750->sysclk, params_rate(params)); + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: break; - case 4: - srate |= (0x1 << 7); + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; break; - case 8: - srate |= (0x2 << 7); + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; break; - case 16: - srate |= (0x3 << 7); + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x000c; break; } /* set iface & srate */ wm8750_write(codec, WM8750_IFACE, iface); - wm8750_write(codec, WM8750_SRATE, srate | - (coeff_div[i].sr << 1) | coeff_div[i].usb); + if (coeff >= 0) + wm8750_write(codec, WM8750_SRATE, srate | + (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); return 0; } -static int wm8750_mute(struct snd_soc_codec *codec, - struct snd_soc_codec_dai *dai, int mute) +static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute) { + struct snd_soc_codec *codec = dai->codec; u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7; + if (mute) wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8); else @@ -974,26 +714,34 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event) return 0; } +#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + struct snd_soc_codec_dai wm8750_dai = { .name = "WM8750", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - }, + .rates = WM8750_RATES, + .formats = WM8750_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, - }, - .config_sysclk = wm8750_config_sysclk, - .digital_mute = wm8750_mute, + .rates = WM8750_RATES, + .formats = WM8750_FORMATS,}, .ops = { - .prepare = wm8750_pcm_prepare, + .hw_params = wm8750_pcm_hw_params, }, - .caps = { - .num_modes = ARRAY_SIZE(wm8750_modes), - .mode = wm8750_modes, + .dai_ops = { + .digital_mute = wm8750_mute, + .set_fmt = wm8750_set_dai_fmt, + .set_sysclk = wm8750_set_dai_sysclk, }, }; EXPORT_SYMBOL_GPL(wm8750_dai); @@ -1037,8 +785,7 @@ static int wm8750_resume(struct platform_device *pdev) if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D0; - schedule_delayed_work(&codec->delayed_work, - msecs_to_jiffies(1000)); + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); } return 0; @@ -1218,6 +965,7 @@ static int wm8750_probe(struct platform_device *pdev) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct wm8750_setup_data *setup = socdev->codec_data; struct snd_soc_codec *codec; + struct wm8750_priv *wm8750; int ret = 0; info("WM8750 Audio Codec %s", WM8750_VERSION); @@ -1225,12 +973,20 @@ static int wm8750_probe(struct platform_device *pdev) if (codec == NULL) return -ENOMEM; + wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL); + if (wm8750 == NULL) { + kfree(codec); + return -ENOMEM; + } + + codec->private_data = wm8750; socdev->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); wm8750_socdev = socdev; INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); + #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) if (setup->i2c_address) { normal_i2c[0] = setup->i2c_address; @@ -1246,6 +1002,25 @@ static int wm8750_probe(struct platform_device *pdev) return ret; } +/* + * This function forces any delayed work to be queued and run. + */ +static int run_delayed_work(struct delayed_work *dwork) +{ + int ret; + + /* cancel any work waiting to be queued. */ + ret = cancel_delayed_work(dwork); + + /* if there was any work waiting then we run it now and + * wait for it's completion */ + if (ret) { + schedule_delayed_work(dwork, 0); + flush_scheduled_work(); + } + return ret; +} + /* power down chip */ static int wm8750_remove(struct platform_device *pdev) { @@ -1254,12 +1029,13 @@ static int wm8750_remove(struct platform_device *pdev) if (codec->control_data) wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); - flush_scheduled_work(); + run_delayed_work(&codec->delayed_work); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) i2c_del_driver(&wm8750_i2c_driver); #endif + kfree(codec->private_data); kfree(codec); return 0; diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h index ee5eea4a2d34..a97a54a6348e 100644 --- a/sound/soc/codecs/wm8750.h +++ b/sound/soc/codecs/wm8750.h @@ -55,9 +55,10 @@ #define WM8750_CACHE_REGNUM 0x2a +#define WM8750_SYSCLK 0 + struct wm8750_setup_data { unsigned short i2c_address; - unsigned int mclk; }; extern struct snd_soc_codec_dai wm8750_dai; -- cgit v1.2.3-59-g8ed1b From cbe83b1795feea33803dc89fce18b2b98abbcc9b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:16:02 +0100 Subject: [ALSA] soc - ASoC 0.13 WM9712 codec driver This patch updates the WM9712 codec driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm9712.c | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index b2d2d03b9544..92a64871bcd0 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -34,23 +34,6 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); -#define AC97_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define AC97_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000) - -/* may need to expand this */ -static struct snd_soc_dai_mode ac97_modes[] = { - { - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE, - .pcmrate = AC97_RATES, - .pcmdir = AC97_DIR, - }, -}; - /* * WM9712 register cache */ @@ -554,35 +537,38 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream) return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); } +#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + struct snd_soc_codec_dai wm9712_dai[] = { { .name = "AC97 HiFi", .playback = { .stream_name = "HiFi Playback", .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = WM9712_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .stream_name = "HiFi Capture", .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = WM9712_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .prepare = ac97_prepare,}, - .caps = { - .num_modes = ARRAY_SIZE(ac97_modes), - .mode = ac97_modes,}, - }, - { +}, +{ .name = "AC97 Aux", .playback = { .stream_name = "Aux Playback", .channels_min = 1, - .channels_max = 1,}, + .channels_max = 1, + .rates = WM9712_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .prepare = ac97_aux_prepare,}, - .caps = { - .num_modes = ARRAY_SIZE(ac97_modes), - .mode = ac97_modes,}, - }, +} }; EXPORT_SYMBOL_GPL(wm9712_dai); -- cgit v1.2.3-59-g8ed1b From 5a8ec343c5ba1e78ba23bebd9ad4b23f39c50828 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:16:41 +0100 Subject: [ALSA] soc - ASoC 0.13 generic AC97 codec This patch updates the AC97 codec driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/ac97.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index dd1a9f579a6b..55bc55eb6e24 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -26,22 +26,7 @@ #include #include -#define AC97_VERSION "0.5" - -#define AC97_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define AC97_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000) - -/* may need to expand this */ -static struct snd_soc_dai_mode soc_ac97[] = { - {0, 0, SNDRV_PCM_FMTBIT_S16_LE, AC97_RATES}, - {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, AC97_RATES}, - {0, 0, SNDRV_PCM_FMTBIT_S20_3LE, AC97_RATES}, -}; +#define AC97_VERSION "0.6" static int ac97_prepare(struct snd_pcm_substream *substream) { @@ -55,21 +40,25 @@ static int ac97_prepare(struct snd_pcm_substream *substream) return snd_ac97_set_rate(codec->ac97, reg, runtime->rate); } +#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + static struct snd_soc_codec_dai ac97_dai = { .name = "AC97 HiFi", .playback = { .stream_name = "AC97 Playback", .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = STD_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .stream_name = "AC97 Capture", .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = STD_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .prepare = ac97_prepare,}, - .caps = { - .num_modes = ARRAY_SIZE(soc_ac97), - .mode = soc_ac97,}, }; static unsigned int ac97_read(struct snd_soc_codec *codec, -- cgit v1.2.3-59-g8ed1b From d3d35adc79aa2e48e8177a9506e9bcb5eebba406 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 2 Feb 2007 17:17:39 +0100 Subject: [ALSA] soc - ASoC 0.13 AT91xxxx slave patch This patch adds support for I2S slave mode for the ETI_B1 machine from Endrelia. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/Kconfig | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig index d38ba9203bd9..5bcf08b728b0 100644 --- a/sound/soc/at91/Kconfig +++ b/sound/soc/at91/Kconfig @@ -13,12 +13,20 @@ config SND_AT91_SOC_I2S tristate config SND_AT91_SOC_ETI_B1_WM8731 - tristate "SoC I2S Audio support for Endrelia ETI-B1 board" - depends on SND_AT91_SOC && MACH_ETI_B1 + tristate "SoC I2S Audio support for WM8731-based Endrelia ETI-B1 boards" + depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1) select SND_AT91_SOC_I2S select SND_SOC_WM8731 help - Say Y if you want to add support for SoC audio on Endrelia - ETI-B1 board. + Say Y if you want to add support for SoC audio on WM8731-based + Endrelia Technologies Inc ETI-B1 or ETI-C1 boards. + +config SND_AT91_SOC_ETI_SLAVE + bool "Run codec in slave Mode on Endrelia boards" + depends on SND_AT91_SOC_ETI_B1_WM8731 + default n + help + Say Y if you want to run with the AT91 SSC generating the BCLK + and LRC signals on Endrelia boards. endmenu -- cgit v1.2.3-59-g8ed1b From 171eb8f81d7b0706c1085d272e4955251ed9f05f Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 2 Feb 2007 17:18:38 +0100 Subject: [ALSA] soc - ASoC 0.13 AT91xxxx I2S This patch updates the AT91xxxx I2S driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91-i2s.c | 618 ++++++++++++++++++++++++---------------------- sound/soc/at91/at91-i2s.h | 27 ++ 2 files changed, 356 insertions(+), 289 deletions(-) create mode 100644 sound/soc/at91/at91-i2s.h diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-i2s.c index 876d391c7013..fcc544a96ba3 100644 --- a/sound/soc/at91/at91-i2s.c +++ b/sound/soc/at91/at91-i2s.c @@ -12,8 +12,6 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * - * Revision history - * 3rd Mar 2006 Initial version. */ #include @@ -34,6 +32,7 @@ #include #include "at91-pcm.h" +#include "at91-i2s.h" #if 0 #define DBG(x...) printk(KERN_DEBUG "at91-i2s:" x) @@ -48,65 +47,6 @@ #endif -#define AT91_I2S_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) - -#define AT91_I2S_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -/* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */ -static struct snd_soc_dai_mode at91_i2s[] = { - - /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ - { - .fmt = AT91_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = AT91_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1500, - .bfs = SND_SOC_FSBD(10), - .priv = (25 << 16 | 74), - }, - - /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { - .fmt = AT91_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = AT91_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 750, - .bfs = SND_SOC_FSBD(3), - .priv = (7 << 16 | 133), - }, - - /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ - { - .fmt = AT91_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_32000, - .pcmdir = AT91_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 375, - .bfs = SND_SOC_FSBD(3), - .priv = (7 << 16 | 66), - }, - - /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ - { - .fmt = AT91_I2S_DAIFMT, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = AT91_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 250, - .bfs = SND_SOC_FSBD(5), - .priv = (13 << 16 | 23), - }, -}; - - /* * SSC PDC registers required by the PCM DMA engine. */ @@ -184,21 +124,6 @@ static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { #endif }; - -/* - * A MUTEX is used to protect an SSC initialzed flag which allows - * the substream hw_params() call to initialize the SSC only if - * there are no other substreams open. If there are other - * substreams open, the hw_param() call can only check that - * it is using the same format and rate. - */ -static DECLARE_MUTEX(ssc0_mutex); -#if NUM_SSC_DEVICES == 3 -static DECLARE_MUTEX(ssc1_mutex); -static DECLARE_MUTEX(ssc2_mutex); -#endif - - struct at91_ssc_state { u32 ssc_cmr; u32 ssc_rcmr; @@ -209,16 +134,16 @@ struct at91_ssc_state { u32 ssc_imr; }; - static struct at91_ssc_info { char *name; struct at91_ssc_periph ssc; spinlock_t lock; /* lock for dir_mask */ - int dir_mask; /* 0=unused, 1=playback, 2=capture */ - struct semaphore *mutex; - int initialized; - int pcmfmt; - int rate; + unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */ + unsigned short initialized; /* 1=SSC has been initialized */ + unsigned short daifmt; + unsigned short cmr_div; + unsigned short tcmr_period; + unsigned short rcmr_period; struct at91_pcm_dma_params *dma_params[2]; struct at91_ssc_state ssc_state; @@ -227,7 +152,6 @@ static struct at91_ssc_info { .name = "ssc0", .lock = SPIN_LOCK_UNLOCKED, .dir_mask = 0, - .mutex = &ssc0_mutex, .initialized = 0, }, #if NUM_SSC_DEVICES == 3 @@ -235,20 +159,23 @@ static struct at91_ssc_info { .name = "ssc1", .lock = SPIN_LOCK_UNLOCKED, .dir_mask = 0, - .mutex = &ssc1_mutex, .initialized = 0, }, { .name = "ssc2", .lock = SPIN_LOCK_UNLOCKED, .dir_mask = 0, - .mutex = &ssc2_mutex, .initialized = 0, }, #endif }; +static unsigned int at91_i2s_sysclk; +/* + * SSC interrupt handler. Passes PDC interrupts to the DMA + * interrupt handler in the PCM driver. + */ static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) { struct at91_ssc_info *ssc_p = dev_id; @@ -278,10 +205,13 @@ static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * Startup. Only that one substream allowed in each direction. + */ static int at91_i2s_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct at91_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; + struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; int dir_mask; DBG("i2s_startup: SSC_SR=0x%08lx\n", @@ -296,24 +226,22 @@ static int at91_i2s_startup(struct snd_pcm_substream *substream) ssc_p->dir_mask |= dir_mask; spin_unlock_irq(&ssc_p->lock); - /* - * dma_data is not set until hw_params() is called and - * shutdown() depends on this value being NULL if hw_params() - * was not called. - */ - rtd->cpu_dai->dma_data = NULL; - return 0; } +/* + * Shutdown. Clear DMA parameters and shutdown the SSC if there + * are no other substreams open. + */ static void at91_i2s_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct at91_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; - struct at91_pcm_dma_params *dma_params = rtd->cpu_dai->dma_data; + struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; + struct at91_pcm_dma_params *dma_params; int dir, dir_mask; dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + dma_params = ssc_p->dma_params[dir]; if (dma_params != NULL) { at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, @@ -335,99 +263,107 @@ static void at91_i2s_shutdown(struct snd_pcm_substream *substream) DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); at91_sys_write(AT91_PMC_PCDR, 1<ssc.pid); - if (ssc_p->initialized) + if (ssc_p->initialized) { free_irq(ssc_p->ssc.pid, ssc_p); + ssc_p->initialized = 0; + } /* Reset the SSC */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); - /* Force a re-init on the next hw_params() call. */ - ssc_p->initialized = 0; + /* Clear the SSC dividers */ + ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; } spin_unlock_irq(&ssc_p->lock); } -#ifdef CONFIG_PM -static int at91_i2s_suspend(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) +/* + * Record the SSC system clock rate. + */ +static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) { - struct at91_ssc_info *ssc_p; - - if(!dai->active) - return 0; - - ssc_p = &ssc_info[dai->id]; - - /* Save the status register before disabling transmit and receive. */ - ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); - at91_ssc_write(ssc_p->ssc.base + - AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); - - /* Save the current interrupt mask, then disable unmasked interrupts. */ - ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr); - - ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); - ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); - ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + /* + * The only clock supplied to the SSC is the AT91 master clock, + * which is only used if the SSC is generating BCLK and/or + * LRC clocks. + */ + switch (clk_id) { + case AT91_SYSCLK_MCK: + at91_i2s_sysclk = freq; + break; + default: + return -EINVAL; + } return 0; } -static int at91_i2s_resume(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai) +/* + * Record the DAI format for use in hw_params(). + */ +static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) { - struct at91_ssc_info *ssc_p; - u32 cr_mask; - - if(!dai->active) - return 0; - - ssc_p = &ssc_info[dai->id]; + struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_tfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_tcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr); - - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr); - - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, - ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | - ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) + return -EINVAL; + ssc_p->daifmt = fmt; return 0; } -#else -#define at91_i2s_suspend NULL -#define at91_i2s_resume NULL -#endif - -static unsigned int at91_i2s_config_sysclk( - struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info, - unsigned int clk) +/* + * Record SSC clock dividers for use in hw_params(). + */ +static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai, + int div_id, int div) { - /* Currently, there is only support for USB (12Mhz) mode */ - if (clk != 12000000) - return 0; - return 12000000; + struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; + + switch (div_id) { + case AT91SSC_CMR_DIV: + /* + * The same master clock divider is used for both + * transmit and receive, so if a value has already + * been set, it must match this value. + */ + if (ssc_p->cmr_div == 0) + ssc_p->cmr_div = div; + else + if (div != ssc_p->cmr_div) + return -EBUSY; + break; + + case AT91SSC_TCMR_PERIOD: + ssc_p->tcmr_period = div; + break; + + case AT91SSC_RCMR_PERIOD: + ssc_p->rcmr_period = div; + break; + + default: + return -EINVAL; + } + + return 0; } +/* + * Configure the SSC. + */ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - int id = rtd->cpu_dai->id; + int id = rtd->dai->cpu_dai->id; struct at91_ssc_info *ssc_p = &ssc_info[id]; struct at91_pcm_dma_params *dma_params; - unsigned int pcmfmt, rate; int dir, channels, bits; - struct clk *mck_clk; - u32 div, period, tfmr, rfmr, tcmr, rcmr; + u32 tfmr, rfmr, tcmr, rcmr; + int start_event; int ret; /* @@ -442,44 +378,134 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, dma_params->substream = substream; ssc_p->dma_params[dir] = dma_params; - rtd->cpu_dai->dma_data = dma_params; - rate = params_rate(params); - channels = params_channels(params); + /* + * The cpu_dai->dma_data field is only used to communicate the + * appropriate DMA parameters to the pcm driver hw_params() + * function. It should not be used for other purposes + * as it is common to all substreams. + */ + rtd->dai->cpu_dai->dma_data = dma_params; - pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt; - switch (pcmfmt) { - case SNDRV_PCM_FMTBIT_S16_LE: - /* likely this is all we'll ever support, but ... */ - bits = 16; - dma_params->pdc_xfer_size = 2; - break; - default: - printk(KERN_WARNING "at91-i2s: unsupported format %x\n", - pcmfmt); - return -EINVAL; - } + channels = params_channels(params); - /* Don't allow both SSC substreams to initialize at the same time. */ - down(ssc_p->mutex); + /* + * The SSC only supports up to 16-bit samples in I2S format, due + * to the size of the Frame Mode Register FSLEN field. Also, I2S + * implies signed data. + */ + bits = 16; + dma_params->pdc_xfer_size = 2; /* - * If this SSC is alreadly initialized, then this substream must use - * the same format and rate. + * Compute SSC register settings. */ - if (ssc_p->initialized) { - if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) { - printk(KERN_WARNING "at91-i2s: " - "incompatible substream in other direction\n"); - up(ssc_p->mutex); - return -EINVAL; - } - } else { + switch (ssc_p->daifmt) { + case SND_SOC_DAIFMT_CBS_CFS: + /* + * SSC provides BCLK and LRC clocks. + * + * The SSC transmit and receive clocks are generated from the + * MCK divider, and the BCLK signal is output on the SSC TK line. + */ + rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) + | (((bits - 1) << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_LOOP) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) + | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); + + tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( 0 << 23) & AT91_SSC_FSDEN) + | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) + | (((bits - 1) << 16) & AT91_SSC_FSLEN) + | (((channels - 1) << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_DATDEF) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + break; + + case SND_SOC_DAIFMT_CBM_CFM: + + /* + * CODEC supplies BCLK and LRC clocks. + * + * The SSC transmit clock is obtained from the BCLK signal on + * on the TK line, and the SSC receive clock is generated from the + * transmit clock. + * + * For single channel data, one sample is transferred on the falling + * edge of the LRC clock. For two channel data, one sample is + * transferred on both edges of the LRC clock. + */ + start_event = channels == 1 + ? AT91_SSC_START_FALLING_RF + : AT91_SSC_START_EDGE_RF; + + rcmr = (( 0 << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( start_event ) & AT91_SSC_START) + | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); + + rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (( 0 << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_LOOP) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + + tcmr = (( 0 << 24) & AT91_SSC_PERIOD) + | (( 1 << 16) & AT91_SSC_STTDLY) + | (( start_event ) & AT91_SSC_START) + | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) + | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) + | (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS); + + tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) + | (( 0 << 23) & AT91_SSC_FSDEN) + | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) + | (( 0 << 16) & AT91_SSC_FSLEN) + | (( 0 << 8) & AT91_SSC_DATNB) + | (( 1 << 7) & AT91_SSC_MSBF) + | (( 0 << 5) & AT91_SSC_DATDEF) + | (((bits - 1) << 0) & AT91_SSC_DATALEN); + break; + + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + printk(KERN_WARNING "at91-i2s: unsupported DAI format 0x%x.\n", + ssc_p->daifmt); + return -EINVAL; + break; + } + DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr); + + if (!ssc_p->initialized) { + /* Enable PMC peripheral clock for this SSC */ DBG("Starting pid %d clock\n", ssc_p->ssc.pid); at91_sys_write(AT91_PMC_PCER, 1<ssc.pid); - /* Reset the SSC */ + /* Reset the SSC and its PDC registers */ at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); at91_ssc_write(ssc_p->ssc.base + AT91_PDC_RPR, 0); @@ -491,97 +517,30 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNPR, 0); at91_ssc_write(ssc_p->ssc.base + AT91_PDC_TNCR, 0); - div = rtd->cpu_dai->dai_runtime.priv >> 16; - period = rtd->cpu_dai->dai_runtime.priv & 0xffff; - - mck_clk = clk_get(NULL, "mck"); - - DBG("mck %lu fsbd %u bfs %llu bfs_real %u bclk %lu div %u period %u\n", - clk_get_rate(mck_clk), - SND_SOC_FSBD(6), - rtd->cpu_dai->dai_runtime.bfs, - SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), - clk_get_rate(mck_clk) / (2 * div), - div, - period); - - clk_put(mck_clk); - - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, div); - - /* - * Setup the TFMR and RFMR for the proper data format. - */ - tfmr = - (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) - | (( 0 << 23) & AT91_SSC_FSDEN) - | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) - | (((bits - 1) << 16) & AT91_SSC_FSLEN) - | (((channels - 1) << 8) & AT91_SSC_DATNB) - | (( 1 << 7) & AT91_SSC_MSBF) - | (( 0 << 5) & AT91_SSC_DATDEF) - | (((bits - 1) << 0) & AT91_SSC_DATALEN); - DBG("SSC_TFMR=0x%08x\n", tfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr); - - rfmr = - (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) - | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) - | (( 0 << 16) & AT91_SSC_FSLEN) - | (((channels - 1) << 8) & AT91_SSC_DATNB) - | (( 1 << 7) & AT91_SSC_MSBF) - | (( 0 << 5) & AT91_SSC_LOOP) - | (((bits - 1) << 0) & AT91_SSC_DATALEN); - - DBG("SSC_RFMR=0x%08x\n", rfmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr); - - /* - * Setup the TCMR and RCMR to generate the proper BCLK - * and LRC signals. - */ - tcmr = - (( period << 24) & AT91_SSC_PERIOD) - | (( 1 << 16) & AT91_SSC_STTDLY) - | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) - | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) - | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) - | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); - - DBG("SSC_TCMR=0x%08x\n", tcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr); - - rcmr = - (( 0 << 24) & AT91_SSC_PERIOD) - | (( 1 << 16) & AT91_SSC_STTDLY) - | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START) - | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) - | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) - | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); - - DBG("SSC_RCMR=0x%08x\n", rcmr); - at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr); - if ((ret = request_irq(ssc_p->ssc.pid, at91_i2s_interrupt, 0, ssc_p->name, ssc_p)) < 0) { printk(KERN_WARNING "at91-i2s: request_irq failure\n"); + + DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); + at91_sys_write(AT91_PMC_PCER, 1<ssc.pid); return ret; } - /* - * Save the current substream parameters in order to check - * that the substream in the opposite direction uses the - * same parameters. - */ - ssc_p->pcmfmt = pcmfmt; - ssc_p->rate = rate; ssc_p->initialized = 1; - - DBG("hw_params: SSC initialized\n"); } - up(ssc_p->mutex); + /* set SSC clock mode register */ + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div); + + /* set receive clock mode and format */ + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr); + + /* set transmit clock mode and format */ + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr); + DBG("hw_params: SSC initialized\n"); return 0; } @@ -589,39 +548,112 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream, static int at91_i2s_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct at91_pcm_dma_params *dma_params = rtd->cpu_dai->dma_data; + struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; + struct at91_pcm_dma_params *dma_params; + int dir; + + dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + dma_params = ssc_p->dma_params[dir]; at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, dma_params->mask->ssc_enable); - DBG("%s enabled SSC_SR=0x%08lx\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive", - at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc.base + AT91_SSC_SR)); + DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit", + at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR)); return 0; } +#ifdef CONFIG_PM +static int at91_i2s_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + struct at91_ssc_info *ssc_p; + + if(!cpu_dai->active) + return 0; + + ssc_p = &ssc_info[cpu_dai->id]; + + /* Save the status register before disabling transmit and receive. */ + ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, + AT91_SSC_TXDIS | AT91_SSC_RXDIS); + + /* Save the current interrupt mask, then disable unmasked interrupts. */ + ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr); + + ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); + ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); + ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR); + ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR); + ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR); + + return 0; +} + +static int at91_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + struct at91_ssc_info *ssc_p; + + if(!cpu_dai->active) + return 0; + + ssc_p = &ssc_info[cpu_dai->id]; + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr); + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr); + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr); + + at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, + ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | + ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); + + return 0; +} + +#else +#define at91_i2s_suspend NULL +#define at91_i2s_resume NULL +#endif + +#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) + struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { { .name = "at91_ssc0/i2s", .id = 0, .type = SND_SOC_DAI_I2S, .suspend = at91_i2s_suspend, .resume = at91_i2s_resume, - .config_sysclk = at91_i2s_config_sysclk, .playback = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .startup = at91_i2s_startup, .shutdown = at91_i2s_shutdown, .prepare = at91_i2s_prepare, .hw_params = at91_i2s_hw_params,}, - .caps = { - .mode = &at91_i2s[0], - .num_modes = ARRAY_SIZE(at91_i2s),}, + .dai_ops = { + .set_sysclk = at91_i2s_set_dai_sysclk, + .set_fmt = at91_i2s_set_dai_fmt, + .set_clkdiv = at91_i2s_set_dai_clkdiv,}, .private_data = &ssc_info[0].ssc, }, #if NUM_SSC_DEVICES == 3 @@ -630,21 +662,25 @@ struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { .type = SND_SOC_DAI_I2S, .suspend = at91_i2s_suspend, .resume = at91_i2s_resume, - .config_sysclk = at91_i2s_config_sysclk, .playback = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .startup = at91_i2s_startup, .shutdown = at91_i2s_shutdown, .prepare = at91_i2s_prepare, .hw_params = at91_i2s_hw_params,}, - .caps = { - .mode = &at91_i2s[0], - .num_modes = ARRAY_SIZE(at91_i2s),}, + .dai_ops = { + .set_sysclk = at91_i2s_set_dai_sysclk, + .set_fmt = at91_i2s_set_dai_fmt, + .set_clkdiv = at91_i2s_set_dai_clkdiv,}, .private_data = &ssc_info[1].ssc, }, { .name = "at91_ssc2/i2s", @@ -652,21 +688,25 @@ struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { .type = SND_SOC_DAI_I2S, .suspend = at91_i2s_suspend, .resume = at91_i2s_resume, - .config_sysclk = at91_i2s_config_sysclk, .playback = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 1, - .channels_max = 2,}, + .channels_max = 2, + .rates = AT91_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .startup = at91_i2s_startup, .shutdown = at91_i2s_shutdown, .prepare = at91_i2s_prepare, .hw_params = at91_i2s_hw_params,}, - .caps = { - .mode = &at91_i2s[0], - .num_modes = ARRAY_SIZE(at91_i2s),}, + .dai_ops = { + .set_sysclk = at91_i2s_set_dai_sysclk, + .set_fmt = at91_i2s_set_dai_fmt, + .set_clkdiv = at91_i2s_set_dai_clkdiv,}, .private_data = &ssc_info[2].ssc, }, #endif diff --git a/sound/soc/at91/at91-i2s.h b/sound/soc/at91/at91-i2s.h new file mode 100644 index 000000000000..f8a875ba0ccc --- /dev/null +++ b/sound/soc/at91/at91-i2s.h @@ -0,0 +1,27 @@ +/* + * at91-i2s.h - ALSA I2S interface for the Atmel AT91 SoC + * + * Author: Frank Mandarino + * Endrelia Technologies Inc. + * Created: Jan 9, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _AT91_I2S_H +#define _AT91_I2S_H + +/* I2S system clock ids */ +#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */ + +/* I2S divider ids */ +#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */ +#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */ +#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */ + +extern struct snd_soc_cpu_dai at91_i2s_dai[]; + +#endif /* _AT91_I2S_H */ + -- cgit v1.2.3-59-g8ed1b From 6297027629a9349301e08442b67deb9783a5e984 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 2 Feb 2007 17:19:24 +0100 Subject: [ALSA] soc - ASoC 0.13 AT91xxxx DMA This patch updates the AT91xxxx audio DMA driver to the new API in ASoC 0.13. Changes:- o Updated to use new 0.13 data structures. o Suspend and Resume now conditionally compiled. o #include guard around at91-pcm.h header. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/at91-pcm.c | 7 ++++++- sound/soc/at91/at91-pcm.h | 9 +++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c index fd9d7327b239..e88b12e7cc40 100644 --- a/sound/soc/at91/at91-pcm.c +++ b/sound/soc/at91/at91-pcm.c @@ -125,7 +125,7 @@ static int at91_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - prtd->params = rtd->cpu_dai->dma_data; + prtd->params = rtd->dai->cpu_dai->dma_data; prtd->params->dma_intr_handler = at91_pcm_dma_irq; prtd->dma_buffer = runtime->dma_addr; @@ -363,6 +363,7 @@ static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm) } } +#ifdef CONFIG_PM static int at91_pcm_suspend(struct platform_device *pdev, struct snd_soc_cpu_dai *dai) { @@ -410,6 +411,10 @@ static int at91_pcm_resume(struct platform_device *pdev, at91_ssc_write(params->ssc_base + AT91_PDC_PTCR, params->mask->pdc_enable); return 0; } +#else +#define at91_pcm_suspend NULL +#define at91_pcm_resume NULL +#endif struct snd_soc_platform at91_soc_platform = { .name = "at91-audio", diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h index 6c3b095725c8..58d0f00a07b2 100644 --- a/sound/soc/at91/at91-pcm.h +++ b/sound/soc/at91/at91-pcm.h @@ -16,6 +16,9 @@ * published by the Free Software Foundation. */ +#ifndef _AT91_PCM_H +#define _AT91_PCM_H + #include struct at91_ssc_periph { @@ -23,7 +26,6 @@ struct at91_ssc_periph { u32 pid; }; - /* * Registers and status bits that are required by the PCM driver. */ @@ -44,7 +46,6 @@ struct at91_ssc_mask { u32 pdc_disable; /* PDC recv/trans disable */ }; - /* * This structure, shared between the PCM driver and the interface, * contains all information required by the PCM driver to perform the @@ -63,9 +64,9 @@ struct at91_pcm_dma_params { void (*dma_intr_handler)(u32, struct snd_pcm_substream *); }; -extern struct snd_soc_cpu_dai at91_i2s_dai[3]; extern struct snd_soc_platform at91_soc_platform; - #define at91_ssc_read(a) ((unsigned long) __raw_readl(a)) #define at91_ssc_write(a,v) __raw_writel((v),(a)) + +#endif /* _AT91_PCM_H */ -- cgit v1.2.3-59-g8ed1b From c8044274c7f1e269975b2bd55d057ceb7708e929 Mon Sep 17 00:00:00 2001 From: Frank Mandarino Date: Fri, 2 Feb 2007 17:19:58 +0100 Subject: [ALSA] soc - ASoC 0.13 AT91xxxx Eti_B1 board support This patch updates the EtI B1 machine driver to the new API in ASoC 0.13. Changes:- o Manually configure DAI hardware format. o Removed config_sysclk() function. No longer needed as clocking is now configured manually. Signed-off-by: Frank Mandarino Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/at91/eti_b1_wm8731.c | 141 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 17 deletions(-) diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index 089cdc9e7265..8179df3bb2f3 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -18,9 +18,6 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * - * Revision history - * 30th Nov 2005 Initial version. - * */ #include @@ -43,9 +40,10 @@ #include "../codecs/wm8731.h" #include "at91-pcm.h" +#include "at91-i2s.h" #if 0 -#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731:" x) +#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) #else #define DBG(x...) #endif @@ -57,12 +55,29 @@ #define AT91_PIO_RK1 (1 << (AT91_PIN_PB10 - PIN_BASE) % 32) #define AT91_PIO_RF1 (1 << (AT91_PIN_PB11 - PIN_BASE) % 32) - static struct clk *pck1_clk; static struct clk *pllb_clk; + static int eti_b1_startup(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* cpu clock is the AT91 master clock sent to the SSC */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK, + 60000000, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* codec system clock is supplied by PCK1, set to 12MHz */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, + 12000000, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + /* Start PCK1 clock. */ clk_enable(pck1_clk); DBG("pck1 started\n"); @@ -77,8 +92,105 @@ static void eti_b1_shutdown(struct snd_pcm_substream *substream) DBG("pck1 stopped\n"); } +static int eti_b1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + +#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE + unsigned int rate; + int cmr_div, period; + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* + * The SSC clock dividers depend on the sample rate. The CMR.DIV + * field divides the system master clock MCK to drive the SSC TK + * signal which provides the codec BCLK. The TCMR.PERIOD and + * RCMR.PERIOD fields further divide the BCLK signal to drive + * the SSC TF and RF signals which provide the codec DACLRC and + * ADCLRC clocks. + * + * The dividers were determined through trial and error, where a + * CMR.DIV value is chosen such that the resulting BCLK value is + * divisible, or almost divisible, by (2 * sample rate), and then + * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. + */ + rate = params_rate(params); + + switch (rate) { + case 8000: + cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */ + period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */ + break; + case 32000: + cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */ + period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */ + break; + case 48000: + cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */ + period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */ + break; + default: + printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate); + return -EINVAL; + } + + /* set the MCK divider for BCLK */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div); + if (ret < 0) + return ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* set the BCLK divider for DACLRC */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, + AT91SSC_TCMR_PERIOD, period); + } else { + /* set the BCLK divider for ADCLRC */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, + AT91SSC_RCMR_PERIOD, period); + } + if (ret < 0) + return ret; + +#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ + /* + * Codec in Master Mode. + */ + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + +#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ + + return 0; +} + static struct snd_soc_ops eti_b1_ops = { .startup = eti_b1_startup, + .hw_params = eti_b1_hw_params, .shutdown = eti_b1_shutdown, }; @@ -134,29 +246,19 @@ static int eti_b1_wm8731_init(struct snd_soc_codec *codec) return 0; } -unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - if(info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12000000); - } - return 0; -} - static struct snd_soc_dai_link eti_b1_dai = { .name = "WM8731", .stream_name = "WM8731", .cpu_dai = &at91_i2s_dai[1], .codec_dai = &wm8731_dai, .init = eti_b1_wm8731_init, - .config_sysclk = eti_b1_config_sysclk, + .ops = &eti_b1_ops, }; static struct snd_soc_machine snd_soc_machine_eti_b1 = { .name = "ETI_B1", .dai_link = &eti_b1_dai, .num_links = 1, - .ops = &eti_b1_ops, }; static struct wm8731_setup_data eti_b1_wm8731_setup = { @@ -210,7 +312,7 @@ static int __init eti_b1_init(void) } ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1 - | AT91_PIO_RD1 /* | AT91_PIO_RK1 | AT91_PIO_RF1 */; + | AT91_PIO_RD1 /* | AT91_PIO_RK1 */ | AT91_PIO_RF1; /* Reset all PIO registers and assign lines to peripheral A */ at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines); @@ -237,6 +339,11 @@ static int __init eti_b1_init(void) /* assign the GPIO pin to PCK1 */ at91_set_B_periph(AT91_PIN_PA24, 0); +#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE + printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n"); +#else + printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n"); +#endif return ret; fail_io_unmap: -- cgit v1.2.3-59-g8ed1b From eaff2ae702f937020bfde96eea552caae3815784 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 2 Feb 2007 17:20:40 +0100 Subject: [ALSA] soc - ASoC 0.13 pxa2xx i2s driver This patch updates the pxa2xx I2S driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Removed config_sysclk() function and struct snd_soc_clock_info. No longer needed as clocking is now configured manually in the machine drivers. Also removed other clocking data from structures. o Added pxa2xx-i2s.h header Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-i2s.c | 200 +++++++++++++++++++-------------------------- sound/soc/pxa/pxa2xx-i2s.h | 20 +++++ 2 files changed, 102 insertions(+), 118 deletions(-) create mode 100644 sound/soc/pxa/pxa2xx-i2s.h diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 98b167fe68e5..575a6137c040 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -29,11 +29,7 @@ #include #include "pxa2xx-pcm.h" - -/* used to disable sysclk if external crystal is used */ -static int extclk; -module_param(extclk, int, 0); -MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk"); +#include "pxa2xx-i2s.h" struct pxa_i2s_port { u32 sadiv; @@ -41,97 +37,10 @@ struct pxa_i2s_port { u32 sacr1; u32 saimr; int master; + u32 fmt; }; static struct pxa_i2s_port pxa_i2s; -#define PXA_I2S_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF) - -#define PXA_I2S_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define PXA_I2S_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - -/* priv is divider */ -static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { - /* pxa2xx I2S frame and clock master modes */ - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x48, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x34, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x24, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x1a, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0xd, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0xc, - }, - - /* pxa2xx I2S frame master and clock slave mode */ - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = PXA_I2S_RATES, - .pcmdir = PXA_I2S_DIR, - .fs = SND_SOC_FS_ALL, - .flags = SND_SOC_DAI_BFS_RATE, - .bfs = 64, - .priv = 0x48, - }, -}; - static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { .name = "I2S PCM Stereo out", .dev_addr = __PREG(SADR), @@ -171,8 +80,9 @@ static struct pxa2xx_gpio gpio_bus[] = { static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; - if (!rtd->cpu_dai->active) { + if (!cpu_dai->active) { SACR0 |= SACR0_RST; SACR0 = 0; } @@ -191,18 +101,50 @@ static int pxa_i2s_wait(void) return 0; } -static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + pxa_i2s.fmt = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + pxa_i2s.fmt = SACR1_AMSL; + break; + } - pxa_i2s.master = 0; - if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS) + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: pxa_i2s.master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + pxa_i2s.master = 0; + break; + default: + break; + } + return 0; +} - if (pxa_i2s.master && !extclk) +static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + if (clk_id != PXA2XX_I2S_SYSCLK) + return -ENODEV; + + if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); + return 0; +} + +static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); @@ -211,9 +153,9 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, pxa_i2s_wait(); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; + cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; else - rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; + cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; /* is port used by another stream */ if (!(SACR0 & SACR0_ENB)) { @@ -224,16 +166,37 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, SACR0 |= SACR0_BCKD; SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); - - if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J) - SACR1 |= SACR1_AMSL; + SACR1 |= pxa_i2s.fmt; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) SAIMR |= SAIMR_TFS; else SAIMR |= SAIMR_RFS; - SADIV = rtd->cpu_dai->dai_runtime.priv; + switch (params_rate(params)) { + case 8000: + SADIV = 0x48; + break; + case 11025: + SADIV = 0x34; + break; + case 16000: + SADIV = 0x24; + break; + case 22050: + SADIV = 0x1a; + break; + case 44100: + SADIV = 0xd; + break; + case 48000: + SADIV = 0xc; + break; + case 96000: /* not in manual and possibly slightly inaccurate */ + SADIV = 0x6; + break; + } + return 0; } @@ -316,12 +279,9 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev, #define pxa2xx_i2s_resume NULL #endif -/* pxa2xx I2S sysclock is always 256 FS */ -static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface, - struct snd_soc_clock_info *info, unsigned int clk) -{ - return info->rate << 8; -} +#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) struct snd_soc_cpu_dai pxa_i2s_dai = { .name = "pxa2xx-i2s", @@ -329,21 +289,25 @@ struct snd_soc_cpu_dai pxa_i2s_dai = { .type = SND_SOC_DAI_I2S, .suspend = pxa2xx_i2s_suspend, .resume = pxa2xx_i2s_resume, - .config_sysclk = pxa_i2s_config_sysclk, .playback = { .channels_min = 2, - .channels_max = 2,}, + .channels_max = 2, + .rates = PXA2XX_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 2, - .channels_max = 2,}, + .channels_max = 2, + .rates = PXA2XX_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .startup = pxa2xx_i2s_startup, .shutdown = pxa2xx_i2s_shutdown, .trigger = pxa2xx_i2s_trigger, .hw_params = pxa2xx_i2s_hw_params,}, - .caps = { - .num_modes = ARRAY_SIZE(pxa2xx_i2s_modes), - .mode = pxa2xx_i2s_modes,}, + .dai_ops = { + .set_fmt = pxa2xx_i2s_set_dai_fmt, + .set_sysclk = pxa2xx_i2s_set_dai_sysclk, + }, }; EXPORT_SYMBOL_GPL(pxa_i2s_dai); diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h new file mode 100644 index 000000000000..a2484f0881f1 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-i2s.h @@ -0,0 +1,20 @@ +/* + * linux/sound/arm/pxa2xx-i2s.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _PXA2XX_I2S_H +#define _PXA2XX_I2S_H + +/* pxa2xx DAI ID's */ +#define PXA2XX_DAI_I2S 0 + +/* I2S clock */ +#define PXA2XX_I2S_SYSCLK 0 + +extern struct snd_soc_cpu_dai pxa_i2s_dai; + +#endif -- cgit v1.2.3-59-g8ed1b From 596ce32b74dccf53ef59cc9ba2e95a2a34ba921c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:21:16 +0100 Subject: [ALSA] soc - ASoC 0.13 pxa2xx AC97 driver This patch updates the pxa2xx AC97 driver to the new API in ASoC 0.13. Changes:- o Removed DAI capabilities matching code in favour of manual matching in the machine drivers. o Added DAI operations for codec and CPU interfaces. o Added pxa2xx-ac97.h header Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-ac97.c | 64 ++++++++++++++++++++------------------------- sound/soc/pxa/pxa2xx-ac97.h | 22 ++++++++++++++++ 2 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 sound/soc/pxa/pxa2xx-ac97.h diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 28b1985edc05..1bbbeff84ef0 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -31,27 +31,12 @@ #include #include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" static DEFINE_MUTEX(car_mutex); static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); static volatile long gsr_bits; -#define AC97_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - -#define AC97_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) - -/* may need to expand this */ -static struct snd_soc_dai_mode pxa2xx_ac97_modes[] = { - { - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = AC97_RATES, - .pcmdir = AC97_DIR, - }, -}; - /* * Beware PXA27x bugs: * @@ -334,11 +319,12 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; + cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; else - rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; + cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; return 0; } @@ -347,11 +333,12 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; + cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; else - rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; + cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; return 0; } @@ -360,15 +347,20 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; else - rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; + cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; return 0; } +#define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + /* * There is only 1 physical AC97 interface for pxa2xx, but it * has extra fifo's that can be used for aux DACs and ADCs. @@ -385,16 +377,17 @@ struct snd_soc_cpu_dai pxa_ac97_dai[] = { .playback = { .stream_name = "AC97 Playback", .channels_min = 2, - .channels_max = 2,}, + .channels_max = 2, + .rates = PXA2XX_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .stream_name = "AC97 Capture", .channels_min = 2, - .channels_max = 2,}, + .channels_max = 2, + .rates = PXA2XX_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .hw_params = pxa2xx_ac97_hw_params,}, - .caps = { - .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), - .mode = pxa2xx_ac97_modes,}, }, { .name = "pxa2xx-ac97-aux", @@ -403,16 +396,17 @@ struct snd_soc_cpu_dai pxa_ac97_dai[] = { .playback = { .stream_name = "AC97 Aux Playback", .channels_min = 1, - .channels_max = 1,}, + .channels_max = 1, + .rates = PXA2XX_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .stream_name = "AC97 Aux Capture", .channels_min = 1, - .channels_max = 1,}, + .channels_max = 1, + .rates = PXA2XX_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .hw_params = pxa2xx_ac97_hw_aux_params,}, - .caps = { - .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), - .mode = pxa2xx_ac97_modes,}, }, { .name = "pxa2xx-ac97-mic", @@ -421,12 +415,12 @@ struct snd_soc_cpu_dai pxa_ac97_dai[] = { .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, - .channels_max = 1,}, + .channels_max = 1, + .rates = PXA2XX_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .ops = { .hw_params = pxa2xx_ac97_hw_mic_params,}, - .caps = { - .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes), - .mode = pxa2xx_ac97_modes,},}, +}, }; EXPORT_SYMBOL_GPL(pxa_ac97_dai); diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h new file mode 100644 index 000000000000..4c4b882316ac --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ac97.h @@ -0,0 +1,22 @@ +/* + * linux/sound/arm/pxa2xx-ac97.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _PXA2XX_AC97_H +#define _PXA2XX_AC97_H + +/* pxa2xx DAI ID's */ +#define PXA2XX_DAI_AC97_HIFI 0 +#define PXA2XX_DAI_AC97_AUX 1 +#define PXA2XX_DAI_AC97_MIC 2 + +extern struct snd_soc_cpu_dai pxa_ac97_dai[3]; + +/* platform data */ +extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; + +#endif -- cgit v1.2.3-59-g8ed1b From a8f5d0a5d02cda0183c6e68d6a66d4c6641149a9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 2 Feb 2007 17:21:50 +0100 Subject: [ALSA] soc - ASoC 0.13 pxa2xx DMA This patch updates the pxa2xx I2S driver to the new API in ASoC 0.13. Changes:- o Added check in hw_params to detect buffer less pcms (i.e. BT <--> codec). o Updated structures to new API o Removed DAI's and ac97 ops from PCM header. o Integer hardware constraint added for periods. Signed-off-by: Andrew Johnson Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/pxa2xx-pcm.c | 11 ++++++++++- sound/soc/pxa/pxa2xx-pcm.h | 14 -------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index ff32f892287e..35e8fa3a469c 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -76,13 +76,18 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma = rtd->cpu_dai->dma_data; + struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; size_t totsize = params_buffer_bytes(params); size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; int ret; + /* return if this is a bufferless transfer e.g. + * codec <--> BT codec or GSM modem -- lg FIXME */ + if (!dma) + return 0; + /* this may get called several times by oss emulation * with different params */ if (prtd->params == NULL) { @@ -227,6 +232,10 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) if (ret) goto out; + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL); if (prtd == NULL) { ret = -ENOMEM; diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h index 0b55f070da27..54c9c755e508 100644 --- a/sound/soc/pxa/pxa2xx-pcm.h +++ b/sound/soc/pxa/pxa2xx-pcm.h @@ -28,21 +28,7 @@ struct pxa2xx_gpio { u32 frm; }; -/* pxa2xx DAI ID's */ -#define PXA2XX_DAI_AC97_HIFI 0 -#define PXA2XX_DAI_AC97_AUX 1 -#define PXA2XX_DAI_AC97_MIC 2 -#define PXA2XX_DAI_I2S 0 -#define PXA2XX_DAI_SSP1 0 -#define PXA2XX_DAI_SSP2 1 -#define PXA2XX_DAI_SSP3 2 - -extern struct snd_soc_cpu_dai pxa_ac97_dai[3]; -extern struct snd_soc_cpu_dai pxa_i2s_dai; -extern struct snd_soc_cpu_dai pxa_ssp_dai[3]; - /* platform data */ extern struct snd_soc_platform pxa2xx_soc_platform; -extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; #endif -- cgit v1.2.3-59-g8ed1b From d928b25a89c3154fe6d0e62a83f51c5b621aa099 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:22:20 +0100 Subject: [ALSA] soc - ASoC Sharp corgi machine This patch updates the Sharp corgi machine driver to the new API in ASoC 0.13. Changes:- o Manually configure DAI hardware format. o Removed config_sysclk() function. No longer needed as clocking is now configured manually. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/corgi.c | 84 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 2b1c6e94d1e4..5ee51a994ac3 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -37,6 +37,7 @@ #include "../codecs/wm8731.h" #include "pxa2xx-pcm.h" +#include "pxa2xx-i2s.h" #define CORGI_HP 0 #define CORGI_MIC 1 @@ -119,8 +120,59 @@ static int corgi_shutdown(struct snd_pcm_substream *substream) return 0; } +static int corgi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + clk = 11289600; + break; + } + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set the I2S system clock as input (unused) */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + static struct snd_soc_ops corgi_ops = { .startup = corgi_startup, + .hw_params = corgi_hw_params, .shutdown = corgi_shutdown, }; @@ -264,35 +316,6 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) return 0; } -static unsigned int corgi_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { - /* pxa2xx is i2s master */ - switch (info->rate) { - case 44100: - case 88200: - /* configure codec digital filters for 44.1, 88.2 */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - 11289600); - break; - default: - /* configure codec digital filters for all other rates */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - CORGI_AUDIO_CLOCK); - break; - } - /* config pxa i2s as master */ - return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, - CORGI_AUDIO_CLOCK); - } else { - /* codec is i2s master - - * only configure codec DAI clock and filters */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - CORGI_AUDIO_CLOCK); - } -} - /* corgi digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", @@ -300,7 +323,7 @@ static struct snd_soc_dai_link corgi_dai = { .cpu_dai = &pxa_i2s_dai, .codec_dai = &wm8731_dai, .init = corgi_wm8731_init, - .config_sysclk = corgi_config_sysclk, + .ops = &corgi_ops, }; /* corgi audio machine driver */ @@ -308,7 +331,6 @@ static struct snd_soc_machine snd_soc_machine_corgi = { .name = "Corgi", .dai_link = &corgi_dai, .num_links = 1, - .ops = &corgi_ops, }; /* corgi audio private data */ -- cgit v1.2.3-59-g8ed1b From 97952f601e939278df1194bac56b9755338ee7c1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:22:46 +0100 Subject: [ALSA] soc - ASoC 0.13 spitz machine This patch updates the Sharp spitz machine driver to the new API in ASoC 0.13. Changes:- o Manually configure DAI hardware format. o Removed config_sysclk() function. No longer needed as clocking is now configured manually. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/spitz.c | 86 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 17c8e61efe6f..80e82109fef7 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -37,6 +37,7 @@ #include #include "../codecs/wm8750.h" #include "pxa2xx-pcm.h" +#include "pxa2xx-i2s.h" #define SPITZ_HP 0 #define SPITZ_MIC 1 @@ -121,8 +122,59 @@ static int spitz_startup(struct snd_pcm_substream *substream) return 0; } +static int spitz_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + clk = 11289600; + break; + } + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set the I2S system clock as input (unused) */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + static struct snd_soc_ops spitz_ops = { .startup = spitz_startup, + .hw_params = spitz_hw_params, }; static int spitz_get_jack(struct snd_kcontrol *kcontrol, @@ -276,37 +328,6 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) return 0; } -static unsigned int spitz_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { - /* pxa2xx is i2s master */ - switch (info->rate) { - case 11025: - case 22050: - case 44100: - case 88200: - /* configure codec digital filters - * for 11.025, 22.05, 44.1, 88.2 */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - 11289600); - break; - default: - /* configure codec digital filters for all other rates */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - SPITZ_AUDIO_CLOCK); - break; - } - /* configure pxa2xx i2s interface clocks as master */ - return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, - SPITZ_AUDIO_CLOCK); - } else { - /* codec is i2s master - only configure codec DAI clock */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - SPITZ_AUDIO_CLOCK); - } -} - /* spitz digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", @@ -314,7 +335,7 @@ static struct snd_soc_dai_link spitz_dai = { .cpu_dai = &pxa_i2s_dai, .codec_dai = &wm8750_dai, .init = spitz_wm8750_init, - .config_sysclk = spitz_config_sysclk, + .ops = &spitz_ops, }; /* spitz audio machine driver */ @@ -322,7 +343,6 @@ static struct snd_soc_machine snd_soc_machine_spitz = { .name = "Spitz", .dai_link = &spitz_dai, .num_links = 1, - .ops = &spitz_ops, }; /* spitz audio private data */ -- cgit v1.2.3-59-g8ed1b From cb4c048b9306555ccbdb97eaf7922624664b0eb1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:23:11 +0100 Subject: [ALSA] soc - ASoC 0.13 Sharp tosa machine This patch updates the Sharp tosa machine driver to the new API in ASoC 0.13. Changes:- o Update machine operations to new API. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/tosa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 8c3c6b0534d6..5504e30acf14 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -40,6 +40,7 @@ #include "../codecs/wm9712.h" #include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" static struct snd_soc_machine tosa; @@ -228,12 +229,14 @@ static struct snd_soc_dai_link tosa_dai[] = { .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], .init = tosa_ac97_init, + .ops = &tosa_ops, }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], + .ops = &tosa_ops, }, }; @@ -241,7 +244,6 @@ static struct snd_soc_machine tosa = { .name = "Tosa", .dai_link = tosa_dai, .num_links = ARRAY_SIZE(tosa_dai), - .ops = &tosa_ops, }; static struct snd_soc_device tosa_snd_devdata = { -- cgit v1.2.3-59-g8ed1b From 73f40dc1e147b41eb74bc92ff62bb65cb3260eff Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 2 Feb 2007 17:23:42 +0100 Subject: [ALSA] soc - ASoC 0.13 Sharp poodle machine This patch updates the Sharp poodle machine driver to the new API in ASoC 0.13. Changes:- o Manually configure DAI hardware format. o Removed config_sysclk() function. No longer needed as clocking is now configured manually. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/pxa/poodle.c | 83 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index ee9336086820..0915cf740421 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -34,6 +34,7 @@ #include "../codecs/wm8731.h" #include "pxa2xx-pcm.h" +#include "pxa2xx-i2s.h" #define POODLE_HP 1 #define POODLE_HP_OFF 0 @@ -100,8 +101,59 @@ static int poodle_shutdown(struct snd_pcm_substream *substream) return 0; } +static int poodle_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + clk = 11289600; + break; + } + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set the I2S system clock as input (unused) */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + static struct snd_soc_ops poodle_ops = { .startup = poodle_startup, + .hw_params = poodle_hw_params, .shutdown = poodle_shutdown, }; @@ -225,34 +277,6 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) return 0; } -static unsigned int poodle_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { - /* pxa2xx is i2s master */ - switch (info->rate) { - case 44100: - case 88200: - /* configure codec digital filters for 44.1, 88.2 */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - 11289600); - break; - default: - /* configure codec digital filters for all other rates */ - rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - POODLE_AUDIO_CLOCK); - break; - } - return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, - POODLE_AUDIO_CLOCK); - } else { - /* codec is i2s master - - * only configure codec DAI clock and filters */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, - POODLE_AUDIO_CLOCK); - } -} - /* poodle digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link poodle_dai = { .name = "WM8731", @@ -260,7 +284,7 @@ static struct snd_soc_dai_link poodle_dai = { .cpu_dai = &pxa_i2s_dai, .codec_dai = &wm8731_dai, .init = poodle_wm8731_init, - .config_sysclk = poodle_config_sysclk, + .ops = &poodle_ops, }; /* poodle audio machine driver */ @@ -268,7 +292,6 @@ static struct snd_soc_machine snd_soc_machine_poodle = { .name = "Poodle", .dai_link = &poodle_dai, .num_links = 1, - .ops = &poodle_ops, }; /* poodle audio private data */ -- cgit v1.2.3-59-g8ed1b From f32610edab47f36946d23b883aeae91e15986121 Mon Sep 17 00:00:00 2001 From: Jakub Schmidtke Date: Fri, 2 Feb 2007 18:17:27 +0100 Subject: [ALSA] hda-codec - Add ALC861VD/ALC660VD support o Added ALC861VD support to patch_realtek.c under hda-intel o Added ALC660VD as a model of 861VD o Added pci quirks for Asus G1 as well as for two devices found in Realtek's driver to point at ALC660VD model (3stack-660) o Added pci quirk for Lenovo 3000 C200 - although untested, it should work with ALC861VD 3stack model o Changed preset id = 0x10ec0660 to point at new patch_alc861vd instead of patch_861 o Organised the list of presets Signed-off-by: Jakub Schmidtke Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 7 + sound/pci/hda/patch_realtek.c | 708 +++++++++++++++++++++++- 2 files changed, 709 insertions(+), 6 deletions(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 7d5a1d00259d..4f82145a9390 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -844,6 +844,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. asus-laptop ASUS F2/F3 laptops auto auto-config reading BIOS (default) + ALC861VD/660VD + 3stack 3-jack + 3stack-dig 3-jack with SPDIF OUT + 6stack-dig 6-jack with SPDIF OUT + 3stack-660 3-jack (for ALC660VD) + auto auto-config reading BIOS (default) + CMI9880 minimal 3-jack in back min_fp 3-jack in back, 2-jack in front diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c022e8157c34..583295deaecd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -108,6 +108,16 @@ enum { ALC861_MODEL_LAST, }; +/* ALC861-VD models */ +enum { + ALC660VD_3ST, + ALC861VD_3ST, + ALC861VD_3ST_DIG, + ALC861VD_6ST_DIG, + ALC861VD_AUTO, + ALC861VD_MODEL_LAST, +}; + /* ALC882 models */ enum { ALC882_3ST_DIG, @@ -7924,21 +7934,707 @@ static int patch_alc861(struct hda_codec *codec) return 0; } +/* + * ALC861-VD support + * + * Based on ALC882 + * + * In addition, an independent DAC + */ +#define ALC861VD_DIGOUT_NID 0x06 + +static hda_nid_t alc861vd_dac_nids[4] = { + /* front, surr, clfe, side surr */ + 0x02, 0x03, 0x04, 0x05 +}; + +/* dac_nids for ALC660vd are in a different order - according to + * Realtek's driver. + * This should probably tesult in a different mixer for 6stack models + * of ALC660vd codecs, but for now there is only 3stack mixer + * - and it is the same as in 861vd. + * adc_nids in ALC660vd are (is) the same as in 861vd + */ +static hda_nid_t alc660vd_dac_nids[3] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x04, 0x03 +}; + +static hda_nid_t alc861vd_adc_nids[1] = { + /* ADC0 */ + 0x09, +}; + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ +static struct hda_input_mux alc861vd_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +#define alc861vd_mux_enum_info alc_mux_enum_info +#define alc861vd_mux_enum_get alc_mux_enum_get + +static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + static hda_nid_t capture_mixers[1] = { 0x22 }; + hda_nid_t nid = capture_mixers[adc_idx]; + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx && ! codec->in_resume) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0x7000 : 0x7080; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + v | (imux->items[i].index << 8)); + } + *cur_val = idx; + return 1; +} + +/* + * 2ch mode + */ +static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 6ch mode + */ +static struct hda_verb alc861vd_6stack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc861vd_6stack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static struct hda_channel_mode alc861vd_6stack_modes[2] = { + { 6, alc861vd_6stack_ch6_init }, + { 8, alc861vd_6stack_ch8_init }, +}; + +static struct snd_kcontrol_new alc861vd_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc861vd_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + *FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc861vd_mux_enum_info, + .get = alc861vd_mux_enum_get, + .put = alc861vd_mux_enum_put, + }, + { } /* end */ +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +static struct snd_kcontrol_new alc861vd_6st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + + { } /* end */ +}; + +static struct snd_kcontrol_new alc861vd_3st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc861vd_volume_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of + * the analog-loopback mixer widget + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(8)}, + + /* + * Set up output mixers (0x02 - 0x05) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + { } +}; + +/* + * 3-stack pin configuration: + * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b + */ +static struct hda_verb alc861vd_3stack_init_verbs[] = { + /* + * Set pin mode and muting + */ + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * 6-stack pin configuration: + */ +static struct hda_verb alc861vd_6stack_init_verbs[] = { + /* + * Set pin mode and muting + */ + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* pcm configuration: identiacal with ALC880 */ +#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback +#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture +#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback +#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture + +/* + * configuration and preset + */ +static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { + [ALC660VD_3ST] = "3stack-660", + [ALC861VD_3ST] = "3stack", + [ALC861VD_3ST_DIG] = "3stack-digout", + [ALC861VD_6ST_DIG] = "6stack-digout", + [ALC861VD_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), + SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), + + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_3ST), + {} +}; + +static struct alc_config_preset alc861vd_presets[] = { + [ALC660VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), + .adc_nids = alc861vd_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_6ST_DIG] = { + .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), + .channel_mode = alc861vd_6stack_modes, + .input_mux = &alc861vd_capture_source, + }, +}; + +/* + * BIOS auto configuration + */ +static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, int dac_idx) +{ + /* set as output */ + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +} + +static void alc861vd_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc861vd_auto_set_output_and_unmute(codec, nid, + PIN_OUT, i); + } +} + + +static void alc861vd_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pins[0]; + if (pin) /* connect to front and use dac 0 */ + alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); +} + +#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) +#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID + +static void alc861vd_auto_init_analog_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc861vd_is_input_pin(nid)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + i <= AUTO_PIN_FRONT_MIC ? + PIN_VREF80 : PIN_IN); + if (nid != ALC861VD_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } + } +} + +#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) +#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) + +/* add playback controls from the parsed DAC table */ +/* Based on ALC880 version. But ALC861VD has separate, + * different NIDs for mute/unmute switch and volume control */ +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; + + for (i = 0; i < cfg->line_outs; i++) { + if (! spec->multiout.dac_nids[i]) + continue; + nid_v = alc861vd_idx_to_mixer_vol( + alc880_dac_to_idx( + spec->multiout.dac_nids[i])); + nid_s = alc861vd_idx_to_mixer_switch( + alc880_dac_to_idx( + spec->multiout.dac_nids[i])); + + if (i == 2) { + /* Center/LFE */ + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_v, 1, + 0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_v, 2, + 0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_s, 1, + 2, HDA_INPUT))) < 0) + return err; + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_s, 2, + 2, HDA_INPUT))) < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_v, 3, + 0, HDA_OUTPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_v, 3, + 2, HDA_INPUT))) < 0) + return err; + } + } + return 0; +} + +/* add playback controls for speaker and HP outputs */ +/* Based on ALC880 version. But ALC861VD has separate, + * different NIDs for mute/unmute switch and volume control */ +static int alc861vd_auto_create_extra_out(struct alc_spec *spec, + hda_nid_t pin, const char *pfx) +{ + hda_nid_t nid_v, nid_s; + int err; + char name[32]; + + if (! pin) + return 0; + + if (alc880_is_fixed_pin(pin)) { + nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); + /* specify the DAC as the extra output */ + if (! spec->multiout.hp_nid) + spec->multiout.hp_nid = nid_v; + else + spec->multiout.extra_out_nid[0] = nid_v; + /* control HP volume/switch on the output mixer amp */ + nid_v = alc861vd_idx_to_mixer_vol( + alc880_fixed_pin_idx(pin)); + nid_s = alc861vd_idx_to_mixer_switch( + alc880_fixed_pin_idx(pin)); + + sprintf(name, "%s Playback Volume", pfx); + if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, + HDA_OUTPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", pfx); + if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, + HDA_INPUT))) < 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); + if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, + HDA_OUTPUT))) < 0) + return err; + } + return 0; +} + +/* parse the BIOS configuration and set up the alc_spec + * return 1 if successful, 0 if the proper config is not found, + * or a negative error code + * Based on ALC880 version - had to change it to override + * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ +static int alc861vd_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; + + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc861vd_ignore)) < 0) + return err; + if (! spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ + + if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 || + (err = alc861vd_auto_create_multi_out_ctls(spec, + &spec->autocfg)) < 0 || + (err = alc861vd_auto_create_extra_out(spec, + spec->autocfg.speaker_pins[0], "Speaker")) < 0 || + (err = alc861vd_auto_create_extra_out(spec, + spec->autocfg.hp_pins[0], "Headphone")) < 0 || + (err = alc880_auto_create_analog_input_ctls(spec, + &spec->autocfg)) < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs[spec->num_init_verbs++] + = alc861vd_volume_init_verbs; + + spec->num_mux_defs = 1; + spec->input_mux = &spec->private_imux; + + return 1; +} + +/* additional initialization for auto-configuration model */ +static void alc861vd_auto_init(struct hda_codec *codec) +{ + alc861vd_auto_init_multi_out(codec); + alc861vd_auto_init_hp_out(codec); + alc861vd_auto_init_analog_input(codec); +} + +static int patch_alc861vd(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, + alc861vd_models, + alc861vd_cfg_tbl); + + if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { + printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/" + "ALC861VD, trying auto-probe from BIOS...\n"); + board_config = ALC861VD_AUTO; + } + + if (board_config == ALC861VD_AUTO) { + /* automatic parse from the BIOS config */ + err = alc861vd_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (! err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC861VD_3ST; + } + } + + if (board_config != ALC861VD_AUTO) + setup_preset(spec, &alc861vd_presets[board_config]); + + spec->stream_name_analog = "ALC861VD Analog"; + spec->stream_analog_playback = &alc861vd_pcm_analog_playback; + spec->stream_analog_capture = &alc861vd_pcm_analog_capture; + + spec->stream_name_digital = "ALC861VD Digital"; + spec->stream_digital_playback = &alc861vd_pcm_digital_playback; + spec->stream_digital_capture = &alc861vd_pcm_digital_capture; + + spec->adc_nids = alc861vd_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + + spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; + spec->num_mixers++; + + codec->patch_ops = alc_patch_ops; + + if (board_config == ALC861VD_AUTO) + spec->init_hook = alc861vd_auto_init; + + return 0; +} + /* * patch entries */ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, - { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, + { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", + .patch = patch_alc861 }, + { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, + { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, + { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, + { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, - { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861", - .patch = patch_alc861 }, - { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", - .patch = patch_alc861 }, - { .id = 0x10ec0660, .name = "ALC660", .patch = patch_alc861 }, {} /* terminator */ }; -- cgit v1.2.3-59-g8ed1b From d0b0fac14edf81dc62615cd757e7c73d2059152c Mon Sep 17 00:00:00 2001 From: Bjoern Fay Date: Mon, 5 Feb 2007 12:27:21 +0100 Subject: [ALSA] usbaudio - Add support for Edirol UA-101 Added support for the Edirol UA-101 (only in high-speed mode) by taking the quirks for the UA-1000 and change them accordingly. Changes were made in 'usbaudio.c', 'usbaudio.h', and 'usbquirks.h' MIDI and recording seem to work perfectly (with JACK), but playback gives some few glitches. I think that's the mentioned synchronizing-problem in the UA-1000 quirk ('FIXME: playback must be synchronized to capture'), so I didn't change that. ToDo: Adding Mixer-Support for the built-in control-panel/patch-bay/router. Signed-off-by: Bjoern Fay Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/usb/usbaudio.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/usbaudio.h | 1 + sound/usb/usbquirks.h | 32 ++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 8fd37596e3a1..4dfb91d4398a 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -3077,6 +3077,58 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip, return 0; } +/* + * Create a stream for an Edirol UA-101 interface. + * Copy, paste and modify from Edirol UA-1000 + */ +static int create_ua101_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + const struct snd_usb_audio_quirk *quirk) +{ + static const struct audioformat ua101_format = { + .format = SNDRV_PCM_FORMAT_S32_LE, + .fmt_type = USB_FORMAT_TYPE_I, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + }; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct audioformat *fp; + int stream, err; + + if (iface->num_altsetting != 2) + return -ENXIO; + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + if (alts->extralen != 18 || alts->extra[1] != USB_DT_CS_INTERFACE || + altsd->bNumEndpoints != 1) + return -ENXIO; + + fp = kmemdup(&ua101_format, sizeof(*fp), GFP_KERNEL); + if (!fp) + return -ENOMEM; + + fp->channels = alts->extra[11]; + fp->iface = altsd->bInterfaceNumber; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]); + + stream = (fp->endpoint & USB_DIR_IN) + ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + err = add_audio_endpoint(chip, stream, fp); + if (err < 0) { + kfree(fp); + return err; + } + /* FIXME: playback must be synchronized to capture */ + usb_set_interface(chip->dev, fp->iface, 0); + return 0; +} + static int snd_usb_create_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, const struct snd_usb_audio_quirk *quirk); @@ -3258,6 +3310,7 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, [QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk, [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk, + [QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk, }; if (quirk->type < QUIRK_TYPE_COUNT) { diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 0f4b2b8541d6..2272f45a1867 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -159,6 +159,7 @@ enum quirk_type { QUIRK_AUDIO_FIXED_ENDPOINT, QUIRK_AUDIO_EDIROL_UA700_UA25, QUIRK_AUDIO_EDIROL_UA1000, + QUIRK_AUDIO_EDIROL_UA101, QUIRK_TYPE_COUNT }; diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index a7e9563a01df..25b4ab4f61e7 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -1098,7 +1098,37 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Edirol UA-101 support */ +/* Roland UA-101 in High-Speed Mode only */ +{ + USB_DEVICE(0x0582, 0x007d), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "UA-101", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_EDIROL_UA101 + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_EDIROL_UA101 + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, { /* has ID 0x0081 when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x0080), -- cgit v1.2.3-59-g8ed1b From 66e27788a33636cf0d9bf22eb9d56a7f4ffa3a84 Mon Sep 17 00:00:00 2001 From: Martin Langer Date: Mon, 5 Feb 2007 13:02:35 +0100 Subject: [ALSA] ac97_bus power management This patch adds CONFIG_PM to the ac97_bus driver. Signed-off-by: Martin Langer Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/ac97_bus.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c index 66de2c2f1554..7fa37e15f196 100644 --- a/sound/ac97_bus.c +++ b/sound/ac97_bus.c @@ -26,6 +26,7 @@ static int ac97_bus_match(struct device *dev, struct device_driver *drv) return 1; } +#ifdef CONFIG_PM static int ac97_bus_suspend(struct device *dev, pm_message_t state) { int ret = 0; @@ -45,12 +46,15 @@ static int ac97_bus_resume(struct device *dev) return ret; } +#endif /* CONFIG_PM */ struct bus_type ac97_bus_type = { .name = "ac97", .match = ac97_bus_match, +#ifdef CONFIG_PM .suspend = ac97_bus_suspend, .resume = ac97_bus_resume, +#endif /* CONFIG_PM */ }; static int __init ac97_bus_init(void) -- cgit v1.2.3-59-g8ed1b From 1ab774e049085da6facfaf3b24d54158c3f0f5b3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 5 Feb 2007 13:07:04 +0100 Subject: [ALSA] snd-ak4114: Fix two array overflows Fix the handling of the TXCSB registers cache. There was one array overflow in reg_write() and one in snd_ak4114_reg_write(). Thanks to David Binderman for reporting the latter. The second overflow probably doesn't matter much, given that the function snd_ak4114_reg_write() appears to be never called. I wonder why it exists and why it is exported. Signed-off-by: Jean Delvare Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/i2c/other/ak4114.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index d2b17c83fd33..adbfd5884d06 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -42,8 +42,8 @@ static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char va ak4114->write(ak4114->private_data, reg, val); if (reg <= AK4114_REG_INT1_MASK) ak4114->regmap[reg] = val; - else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4) - ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val; + else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4) + ak4114->txcsb[reg-AK4114_REG_TXCSB0] = val; } static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg) @@ -127,7 +127,8 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char if (reg <= AK4114_REG_INT1_MASK) reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4) - reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val); + reg_write(chip, reg, + (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val); } void snd_ak4114_reinit(struct ak4114 *chip) -- cgit v1.2.3-59-g8ed1b From 88cb42901f1572c95f5933f363cfebd5044c716a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 5 Feb 2007 14:56:20 +0100 Subject: [ALSA] soc - Clean up with kmemdup() Clean up by replacing with kmemdup(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/soc/codecs/wm8731.c | 11 +++-------- sound/soc/codecs/wm8750.c | 12 +++--------- sound/soc/soc-dapm.c | 10 ++-------- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index e6b990507df2..7ca0b5268289 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -543,14 +543,10 @@ static int wm8731_init(struct snd_soc_device *socdev) codec->dapm_event = wm8731_dapm_event; codec->dai = &wm8731_dai; codec->num_dai = 1; - codec->reg_cache_size = ARRAY_SIZE(wm8731_reg); - codec->reg_cache = - kzalloc(sizeof(u16) * ARRAY_SIZE(wm8731_reg), GFP_KERNEL); + codec->reg_cache_size = sizeof(wm8731_reg); + codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL); if (codec->reg_cache == NULL) return -ENOMEM; - memcpy(codec->reg_cache, - wm8731_reg, sizeof(u16) * ARRAY_SIZE(wm8731_reg)); - codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8731_reg); wm8731_reset(codec); @@ -627,12 +623,11 @@ static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; - i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); if (i2c == NULL) { kfree(codec); return -ENOMEM; } - memcpy(i2c, &client_template, sizeof(struct i2c_client)); i2c_set_clientdata(i2c, codec); codec->control_data = i2c; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index c1ffb61ef7f7..7073e8e294fc 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -807,15 +807,10 @@ static int wm8750_init(struct snd_soc_device *socdev) codec->dapm_event = wm8750_dapm_event; codec->dai = &wm8750_dai; codec->num_dai = 1; - codec->reg_cache_size = ARRAY_SIZE(wm8750_reg); - - codec->reg_cache = - kzalloc(sizeof(u16) * ARRAY_SIZE(wm8750_reg), GFP_KERNEL); + codec->reg_cache_size = sizeof(wm8750_reg); + codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KRENEL); if (codec->reg_cache == NULL) return -ENOMEM; - memcpy(codec->reg_cache, wm8750_reg, - sizeof(u16) * ARRAY_SIZE(wm8750_reg)); - codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8750_reg); wm8750_reset(codec); @@ -900,12 +895,11 @@ static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; - i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); if (i2c == NULL) { kfree(codec); return -ENOMEM; } - memcpy(i2c, &client_template, sizeof(struct i2c_client)); i2c_set_clientdata(i2c, codec); codec->control_data = i2c; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d0162a4cb7fd..7caf8c7b0ac5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -87,16 +87,10 @@ module_param(dapm_status, int, 0); MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); /* create a new dapm widget */ -static struct snd_soc_dapm_widget *dapm_cnew_widget( +static inline struct snd_soc_dapm_widget *dapm_cnew_widget( const struct snd_soc_dapm_widget *_widget) { - struct snd_soc_dapm_widget* widget; - widget = kmalloc(sizeof(struct snd_soc_dapm_widget), GFP_KERNEL); - if (!widget) - return NULL; - - memcpy(widget, _widget, sizeof(struct snd_soc_dapm_widget)); - return widget; + return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } /* set up initial codec paths */ -- cgit v1.2.3-59-g8ed1b From 2594d960793f13582c0730a99c5396cded7cf9d9 Mon Sep 17 00:00:00 2001 From: Rolf Stefan Wilke Date: Tue, 6 Feb 2007 19:18:14 +0100 Subject: [ALSA] emu10k1 - Fix STAC9758 front channel For some time now, some users of STAC9758 (emu10k1) would have no sound on their front channels. This can be fixed (at least for me) by unmuting head phone volume and setting it to 0dB before removing the 'Front Playback' control. For details, cf. https://bugtrack.alsa-project.org/alsa-bug/view.php?id=2308 Find the appropriate patch attached. Credits to: Raymond Signed-off-by: Rolf Stefan Wilke Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/emu10k1/emumixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 0981af842b76..4db6e1ca1665 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1582,6 +1582,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, if (emu->ac97->id == AC97_ID_STAC9758) { emu->rear_ac97 = 1; snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT); + snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202); } /* remove unused AC97 controls */ snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202); -- cgit v1.2.3-59-g8ed1b From 6116ea0741abf8f1ef9d93642d985f91c58ff6bf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Feb 2007 14:07:08 +0100 Subject: [ALSA] Fix possible deadlocks in sequencer at removal of ports Fix possible rwsem deadlocks in sequencer code at removal of sequencer ports. The list_lock of port group can be double locked. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/core/seq/seq_ports.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index d88153438d69..eefd1cf872b4 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -245,9 +245,9 @@ static void clear_subscriber_list(struct snd_seq_client *client, list_del(&subs->dest_list); else list_del(&subs->src_list); + up_write(&agrp->list_mutex); unsubscribe_port(c, aport, agrp, &subs->info, 1); kfree(subs); - up_write(&agrp->list_mutex); snd_seq_port_unlock(aport); snd_seq_client_unlock(c); } -- cgit v1.2.3-59-g8ed1b From 288e5c35f96fefb6c5e0dc8838834c94cff616f6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Feb 2007 14:07:45 +0100 Subject: [ALSA] aoa: remove suspend/resume printks This just removes two useless printks. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/fabrics/snd-aoa-fabric-layout.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index fa4a69fcdc27..1b94ba6dd279 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -1077,8 +1077,6 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta { struct layout_dev *ldev = sdev->ofdev.dev.driver_data; - printk("aoa_fabric_layout_suspend()\n"); - if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) ldev->gpio.methods->all_amps_off(&ldev->gpio); @@ -1089,8 +1087,6 @@ static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) { struct layout_dev *ldev = sdev->ofdev.dev.driver_data; - printk("aoa_fabric_layout_resume()\n"); - if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) ldev->gpio.methods->all_amps_restore(&ldev->gpio); -- cgit v1.2.3-59-g8ed1b From 2cf9f0fc69358e15e78f936c220cfe8aa208111d Mon Sep 17 00:00:00 2001 From: Tobin Davis Date: Wed, 7 Feb 2007 16:04:25 +0100 Subject: [ALSA] hda-codec - Add support for Fujitsu PI1556 Realtek ALC880 This patch adds support for the Fujitsu PI1556 laptop. Issue: Volume knob on system maxes out lower than alsamixer (0x35 vs 0x40). Everything else works, and audio quality is good at 0x35. Signed-off-by: Tobin Davis Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + sound/pci/hda/patch_realtek.c | 39 ++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 4f82145a9390..c30ff1bb2d10 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -785,6 +785,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. asus-dig ASUS with SPDIF out asus-dig2 ASUS with SPDIF out (using GPIO2) uniwill 3-jack + fujitsu Fujitsu Laptops (Pi1536) F1734 2-jack lg LG laptop (m1 express dual) lg-lw LG LW20/LW25 laptop diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 583295deaecd..145682b78071 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -52,6 +52,7 @@ enum { ALC880_ASUS_DIG, ALC880_ASUS_W1V, ALC880_ASUS_DIG2, + ALC880_FUJITSU, ALC880_UNIWILL_DIG, ALC880_UNIWILL, ALC880_UNIWILL_P53, @@ -1073,6 +1074,20 @@ static struct snd_kcontrol_new alc880_uniwill_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc880_fujitsu_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), @@ -1388,6 +1403,11 @@ static struct hda_verb alc880_uniwill_p53_init_verbs[] = { { } }; +static struct hda_verb alc880_beep_init_verbs[] = { + { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, + { } +}; + /* toggle speaker-output according to the hp-jack state */ static void alc880_uniwill_automute(struct hda_codec *codec) { @@ -2357,6 +2377,8 @@ static const char *alc880_models[ALC880_MODEL_LAST] = { [ALC880_ASUS_DIG] = "asus-dig", [ALC880_ASUS_DIG2] = "asus-dig2", [ALC880_UNIWILL_DIG] = "uniwill", + [ALC880_UNIWILL_P53] = "uniwill-p53", + [ALC880_FUJITSU] = "fujitsu", [ALC880_F1734] = "F1734", [ALC880_LG] = "lg", [ALC880_LG_LW] = "lg-lw", @@ -2427,6 +2449,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), @@ -2635,7 +2658,21 @@ static struct alc_config_preset alc880_presets[] = { .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), .dac_nids = alc880_asus_dac_nids, .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), - .channel_mode = alc880_w810_modes, + .channel_mode = alc880_threestack_modes, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .init_hook = alc880_uniwill_p53_hp_automute, + }, + [ALC880_FUJITSU] = { + .mixers = { alc880_fujitsu_mixer, + alc880_pcbeep_mixer, }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_p53_init_verbs, + alc880_beep_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, .init_hook = alc880_uniwill_p53_hp_automute, -- cgit v1.2.3-59-g8ed1b From 547ac2ae3890b8e17bcfea4ba8840a10f3496da4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 8 Feb 2007 14:25:39 +0100 Subject: [ALSA] aoa i2sbus: Stop Apple i2s DMA gracefully This fixes the problem of getting extra bytes inserted at the beginning of a recording when using the Apple i2s interface and DBDMA controller. It turns out that we can't just abort the DMA; we have to let it stop at the end of a command, and then wait for the S7 bit to be set before turning off the DBDMA controller. Doing that for playback doesn't seem to be necessary, but doesn't hurt either. We use the technique used by the Darwin driver: make each transfer command branch to a stop command if the S0 status bit is set. Thus we can ask the DMA controller to stop at the end of the current command by setting S0. The interrupt routine now looks at and clears the status word of the DBDMA command ring. This is necessary so it can know when the DBDMA controller has seen that S0 is set, and so when it should look for the DBDMA controller being stopped and S7 being set. This also ended up simplifying the calculation in i2sbus_pcm_pointer. Tested on a 15 inch albook. [Addition by Johannes] I modified this patch and added the suspend/resume bits to it to get my powermac into a decent state when playing sound across suspend to disk that has a different bitrate from what the firmware programs the hardware to. I also added the SNDRV_PCM_INFO_JOINT_DUPLEX flag because it seemed the right thing to do and I was looking at the info stuff. Signed-off-by: Paul Mackerras Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/aoa/soundbus/i2sbus/i2sbus-core.c | 22 +-- sound/aoa/soundbus/i2sbus/i2sbus-pcm.c | 328 +++++++++++++++++++------------- sound/aoa/soundbus/i2sbus/i2sbus.h | 6 + 3 files changed, 210 insertions(+), 146 deletions(-) diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c index e593a1333fe3..e36f6aa448d4 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c @@ -41,8 +41,8 @@ static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, struct dbdma_command_mem *r, int numcmds) { - /* one more for rounding */ - r->size = (numcmds+1) * sizeof(struct dbdma_cmd); + /* one more for rounding, one for branch back, one for stop command */ + r->size = (numcmds + 3) * sizeof(struct dbdma_cmd); /* We use the PCI APIs for now until the generic one gets fixed * enough or until we get some macio-specific versions */ @@ -377,11 +377,8 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) if (i2sdev->sound.pcm) { /* Suspend PCM streams */ snd_pcm_suspend_all(i2sdev->sound.pcm); - /* Probably useless as we handle - * power transitions ourselves */ - snd_power_change_state(i2sdev->sound.pcm->card, - SNDRV_CTL_POWER_D3hot); } + /* Notify codecs */ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { err = 0; @@ -390,7 +387,11 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) if (err) ret = err; } + + /* wait until streams are stopped */ + i2sbus_wait_for_stop_both(i2sdev); } + return ret; } @@ -402,6 +403,9 @@ static int i2sbus_resume(struct macio_dev* dev) int err, ret = 0; list_for_each_entry(i2sdev, &control->list, item) { + /* reset i2s bus format etc. */ + i2sbus_pcm_prepare_both(i2sdev); + /* Notify codecs so they can re-initialize */ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { err = 0; @@ -410,12 +414,6 @@ static int i2sbus_resume(struct macio_dev* dev) if (err) ret = err; } - /* Notify Alsa */ - if (i2sdev->sound.pcm) { - /* Same comment as above, probably useless */ - snd_power_change_state(i2sdev->sound.pcm->card, - SNDRV_CTL_POWER_D0); - } } return ret; diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c index 7c81db7e52c1..c6b42f9bdbc9 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c @@ -125,7 +125,8 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) } /* bus dependent stuff */ hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME; + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_JOINT_DUPLEX; CHECK_RATE(5512); CHECK_RATE(8000); @@ -245,18 +246,78 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) return err; } +static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, + struct pcm_info *pi) +{ + unsigned long flags; + struct completion done; + long timeout; + + spin_lock_irqsave(&i2sdev->low_lock, flags); + if (pi->dbdma_ring.stopping) { + init_completion(&done); + pi->stop_completion = &done; + spin_unlock_irqrestore(&i2sdev->low_lock, flags); + timeout = wait_for_completion_timeout(&done, HZ); + spin_lock_irqsave(&i2sdev->low_lock, flags); + pi->stop_completion = NULL; + if (timeout == 0) { + /* timeout expired, stop dbdma forcefully */ + printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n"); + /* make sure RUN, PAUSE and S0 bits are cleared */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + pi->dbdma_ring.stopping = 0; + timeout = 10; + while (in_le32(&pi->dbdma->status) & ACTIVE) { + if (--timeout <= 0) + break; + udelay(1); + } + } + } + spin_unlock_irqrestore(&i2sdev->low_lock, flags); +} + +#ifdef CONFIG_PM +void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev) +{ + struct pcm_info *pi; + + get_pcm_info(i2sdev, 0, &pi, NULL); + i2sbus_wait_for_stop(i2sdev, pi); + get_pcm_info(i2sdev, 1, &pi, NULL); + i2sbus_wait_for_stop(i2sdev, pi); +} +#endif + static int i2sbus_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static int i2sbus_hw_free(struct snd_pcm_substream *substream) +static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) { + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + struct pcm_info *pi; + + get_pcm_info(i2sdev, in, &pi, NULL); + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); snd_pcm_lib_free_pages(substream); return 0; } +static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) +{ + return i2sbus_hw_free(substream, 0); +} + +static int i2sbus_record_hw_free(struct snd_pcm_substream *substream) +{ + return i2sbus_hw_free(substream, 1); +} + static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) { /* whee. Hard work now. The user has selected a bitrate @@ -264,7 +325,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) * I2S controller appropriately. */ struct snd_pcm_runtime *runtime; struct dbdma_cmd *command; - int i, periodsize; + int i, periodsize, nperiods; dma_addr_t offset; struct bus_info bi; struct codec_info_item *cii; @@ -274,6 +335,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) struct pcm_info *pi, *other; int cnt; int result = 0; + unsigned int cmd, stopaddr; mutex_lock(&i2sdev->lock); @@ -283,6 +345,13 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) result = -EBUSY; goto out_unlock; } + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); + + if (!pi->substream || !pi->substream->runtime) { + result = -EINVAL; + goto out_unlock; + } runtime = pi->substream->runtime; pi->active = 1; @@ -297,24 +366,43 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) i2sdev->rate = runtime->rate; periodsize = snd_pcm_lib_period_bytes(pi->substream); + nperiods = pi->substream->runtime->periods; pi->current_period = 0; /* generate dbdma command ring first */ command = pi->dbdma_ring.cmds; + memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd)); + + /* commands to DMA to/from the ring */ + /* + * For input, we need to do a graceful stop; if we abort + * the DMA, we end up with leftover bytes that corrupt + * the next recording. To do this we set the S0 status + * bit and wait for the DMA controller to stop. Each + * command has a branch condition to + * make it branch to a stop command if S0 is set. + * On input we also need to wait for the S7 bit to be + * set before turning off the DMA controller. + * In fact we do the graceful stop for output as well. + */ offset = runtime->dma_addr; - for (i = 0; i < pi->substream->runtime->periods; - i++, command++, offset += periodsize) { - memset(command, 0, sizeof(struct dbdma_cmd)); - command->command = - cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS); + cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS; + stopaddr = pi->dbdma_ring.bus_cmd_start + + (nperiods + 1) * sizeof(struct dbdma_cmd); + for (i = 0; i < nperiods; i++, command++, offset += periodsize) { + command->command = cpu_to_le16(cmd); + command->cmd_dep = cpu_to_le32(stopaddr); command->phy_addr = cpu_to_le32(offset); command->req_count = cpu_to_le16(periodsize); - command->xfer_status = cpu_to_le16(0); } - /* last one branches back to first */ - command--; - command->command |= cpu_to_le16(BR_ALWAYS); + + /* branch back to beginning of ring */ + command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); + command++; + + /* set stop command */ + command->command = cpu_to_le16(DBDMA_STOP); /* ok, let's set the serial format and stuff */ switch (runtime->format) { @@ -435,16 +523,18 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) return result; } -static struct dbdma_cmd STOP_CMD = { - .command = __constant_cpu_to_le16(DBDMA_STOP), -}; +#ifdef CONFIG_PM +void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev) +{ + i2sbus_pcm_prepare(i2sdev, 0); + i2sbus_pcm_prepare(i2sdev, 1); +} +#endif static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) { struct codec_info_item *cii; struct pcm_info *pi; - int timeout; - struct dbdma_cmd tmp; int result = 0; unsigned long flags; @@ -464,92 +554,50 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) cii->codec->start(cii, pi->substream); pi->dbdma_ring.running = 1; - /* reset dma engine */ - out_le32(&pi->dbdma->control, - 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); - timeout = 100; - while (in_le32(&pi->dbdma->status) & RUN && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR - "i2sbus: error waiting for dma reset\n"); - result = -ENXIO; - goto out_unlock; + if (pi->dbdma_ring.stopping) { + /* Clear the S0 bit, then see if we stopped yet */ + out_le32(&pi->dbdma->control, 1 << 16); + if (in_le32(&pi->dbdma->status) & ACTIVE) { + /* possible race here? */ + udelay(10); + if (in_le32(&pi->dbdma->status) & ACTIVE) { + pi->dbdma_ring.stopping = 0; + goto out_unlock; /* keep running */ + } + } } + /* make sure RUN, PAUSE and S0 bits are cleared */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + + /* set branch condition select register */ + out_le32(&pi->dbdma->br_sel, (1 << 16) | 1); + /* write dma command buffer address to the dbdma chip */ out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); - /* post PCI write */ - mb(); - (void)in_le32(&pi->dbdma->status); - - /* change first command to STOP */ - tmp = *pi->dbdma_ring.cmds; - *pi->dbdma_ring.cmds = STOP_CMD; - - /* set running state, remember that the first command is STOP */ - out_le32(&pi->dbdma->control, RUN | (RUN << 16)); - timeout = 100; - /* wait for STOP to be executed */ - while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR "i2sbus: error waiting for dma stop\n"); - result = -ENXIO; - goto out_unlock; - } - /* again, write dma command buffer address to the dbdma chip, - * this time of the first real command */ - *pi->dbdma_ring.cmds = tmp; - out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); - /* post write */ - mb(); - (void)in_le32(&pi->dbdma->status); - - /* reset dma engine again */ - out_le32(&pi->dbdma->control, - 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); - timeout = 100; - while (in_le32(&pi->dbdma->status) & RUN && timeout--) - udelay(1); - if (timeout <= 0) { - printk(KERN_ERR - "i2sbus: error waiting for dma reset\n"); - result = -ENXIO; - goto out_unlock; - } - /* wake up the chip with the next descriptor */ - out_le32(&pi->dbdma->control, - (RUN | WAKE) | ((RUN | WAKE) << 16)); - /* get the frame count */ + /* initialize the frame count and current period */ + pi->current_period = 0; pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); + /* set the DMA controller running */ + out_le32(&pi->dbdma->control, (RUN << 16) | RUN); + /* off you go! */ break; + case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: if (!pi->dbdma_ring.running) { result = -EALREADY; goto out_unlock; } + pi->dbdma_ring.running = 0; - /* turn off all relevant bits */ - out_le32(&pi->dbdma->control, - (RUN | WAKE | FLUSH | PAUSE) << 16); - { - /* FIXME: move to own function */ - int timeout = 5000; - while ((in_le32(&pi->dbdma->status) & RUN) - && --timeout > 0) - udelay(1); - if (!timeout) - printk(KERN_ERR - "i2sbus: timed out turning " - "off dbdma engine!\n"); - } + /* Set the S0 bit to make the DMA branch to the stop cmd */ + out_le32(&pi->dbdma->control, (1 << 16) | 1); + pi->dbdma_ring.stopping = 1; - pi->dbdma_ring.running = 0; list_for_each_entry(cii, &i2sdev->sound.codec_list, list) if (cii->codec->stop) cii->codec->stop(cii, pi->substream); @@ -574,70 +622,82 @@ static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) fc = in_le32(&i2sdev->intfregs->frame_count); fc = fc - pi->frame_count; - return (bytes_to_frames(pi->substream->runtime, - pi->current_period * - snd_pcm_lib_period_bytes(pi->substream)) - + fc) % pi->substream->runtime->buffer_size; + if (fc >= pi->substream->runtime->buffer_size) + fc %= pi->substream->runtime->buffer_size; + return fc; } static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) { struct pcm_info *pi; - u32 fc; - u32 delta; + u32 fc, nframes; + u32 status; + int timeout, i; + int dma_stopped = 0; + struct snd_pcm_runtime *runtime; spin_lock(&i2sdev->low_lock); get_pcm_info(i2sdev, in, &pi, NULL); - - if (!pi->dbdma_ring.running) { - /* there was still an interrupt pending - * while we stopped. or maybe another - * processor (not the one that was stopping - * the DMA engine) was spinning above - * waiting for the lock. */ + if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping) goto out_unlock; - } - fc = in_le32(&i2sdev->intfregs->frame_count); - /* a counter overflow does not change the calculation. */ - delta = fc - pi->frame_count; - - /* update current_period */ - while (delta >= pi->substream->runtime->period_size) { - pi->current_period++; - delta = delta - pi->substream->runtime->period_size; - } - - if (unlikely(delta)) { - /* Some interrupt came late, so check the dbdma. - * This special case exists to syncronize the frame_count with - * the dbdma transfer, but is hit every once in a while. */ - int period; - - period = (in_le32(&pi->dbdma->cmdptr) - - pi->dbdma_ring.bus_cmd_start) - / sizeof(struct dbdma_cmd); - pi->current_period = pi->current_period - % pi->substream->runtime->periods; - - while (pi->current_period != period) { - pi->current_period++; - pi->current_period %= pi->substream->runtime->periods; - /* Set delta to zero, as the frame_count value is too - * high (otherwise the code path will not be executed). - * This corrects the fact that the frame_count is too - * low at the beginning due to buffering. */ - delta = 0; + i = pi->current_period; + runtime = pi->substream->runtime; + while (pi->dbdma_ring.cmds[i].xfer_status) { + if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT) + /* + * BT is the branch taken bit. If it took a branch + * it is because we set the S0 bit to make it + * branch to the stop command. + */ + dma_stopped = 1; + pi->dbdma_ring.cmds[i].xfer_status = 0; + + if (++i >= runtime->periods) { + i = 0; + pi->frame_count += runtime->buffer_size; } + pi->current_period = i; + + /* + * Check the frame count. The DMA tends to get a bit + * ahead of the frame counter, which confuses the core. + */ + fc = in_le32(&i2sdev->intfregs->frame_count); + nframes = i * runtime->period_size; + if (fc < pi->frame_count + nframes) + pi->frame_count = fc - nframes; } - pi->frame_count = fc - delta; - pi->current_period %= pi->substream->runtime->periods; + if (dma_stopped) { + timeout = 1000; + for (;;) { + status = in_le32(&pi->dbdma->status); + if (!(status & ACTIVE) && (!in || (status & 0x80))) + break; + if (--timeout <= 0) { + printk(KERN_ERR "i2sbus: timed out " + "waiting for DMA to stop!\n"); + break; + } + udelay(1); + } + + /* Turn off DMA controller, clear S0 bit */ + out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); + pi->dbdma_ring.stopping = 0; + if (pi->stop_completion) + complete(pi->stop_completion); + } + + if (!pi->dbdma_ring.running) + goto out_unlock; spin_unlock(&i2sdev->low_lock); /* may call _trigger again, hence needs to be unlocked */ snd_pcm_period_elapsed(pi->substream); return; + out_unlock: spin_unlock(&i2sdev->low_lock); } @@ -718,7 +778,7 @@ static struct snd_pcm_ops i2sbus_playback_ops = { .close = i2sbus_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = i2sbus_hw_params, - .hw_free = i2sbus_hw_free, + .hw_free = i2sbus_playback_hw_free, .prepare = i2sbus_playback_prepare, .trigger = i2sbus_playback_trigger, .pointer = i2sbus_playback_pointer, @@ -788,7 +848,7 @@ static struct snd_pcm_ops i2sbus_record_ops = { .close = i2sbus_record_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = i2sbus_hw_params, - .hw_free = i2sbus_hw_free, + .hw_free = i2sbus_record_hw_free, .prepare = i2sbus_record_prepare, .trigger = i2sbus_record_trigger, .pointer = i2sbus_record_pointer, diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h index ec20ee615d7f..ff29654782c9 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus.h +++ b/sound/aoa/soundbus/i2sbus/i2sbus.h @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -34,6 +35,7 @@ struct dbdma_command_mem { void *space; int size; u32 running:1; + u32 stopping:1; }; struct pcm_info { @@ -45,6 +47,7 @@ struct pcm_info { u32 frame_count; struct dbdma_command_mem dbdma_ring; volatile struct dbdma_regs __iomem *dbdma; + struct completion *stop_completion; }; enum { @@ -101,6 +104,9 @@ i2sbus_tx_intr(int irq, void *devid); extern irqreturn_t i2sbus_rx_intr(int irq, void *devid); +extern void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev); +extern void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev); + /* control specific functions */ extern int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c); -- cgit v1.2.3-59-g8ed1b From c6d6eeeacc2ed0b736f20692ca021324f3b203b3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Feb 2007 14:50:31 +0100 Subject: [ALSA] ca0106 - Add missing sysfs device assignment Added the missing device assignment before creating sysfs tree. This caused the insufficient device permissions. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/ca0106/ca0106_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 6f781b811876..ea6712b63c9f 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1618,6 +1618,8 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci, snd_ca0106_proc_init(chip); #endif + snd_card_set_dev(card, &pci->dev); + if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; -- cgit v1.2.3-59-g8ed1b From 10b98527c34dca3f461256f5fcfff9b3790066e0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 8 Feb 2007 17:06:09 +0100 Subject: [ALSA] ASoC documentation updates This patch updates the documentation for ASoC to reflect the recent changes in API between 0.12.x and 0.13.x Changes:- o Removed all reference to old API's. o Removed references and examples of automatic DAI config and matching. o Fixed 80 char line length on some files. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- Documentation/sound/alsa/soc/DAI.txt | 490 --------------------------- Documentation/sound/alsa/soc/clocking.txt | 273 +-------------- Documentation/sound/alsa/soc/codec.txt | 107 ++---- Documentation/sound/alsa/soc/machine.txt | 3 +- Documentation/sound/alsa/soc/pops_clicks.txt | 12 +- 5 files changed, 48 insertions(+), 837 deletions(-) diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/alsa/soc/DAI.txt index 251545a88693..58cbfd01ea8f 100644 --- a/Documentation/sound/alsa/soc/DAI.txt +++ b/Documentation/sound/alsa/soc/DAI.txt @@ -54,493 +54,3 @@ Common PCM operating modes:- o Mode A - MSB is transmitted on falling edge of first BCLK after FRAME/SYNC. o Mode B - MSB is transmitted on rising edge of FRAME/SYNC. - - -ASoC DAI Configuration -====================== - -Every CODEC DAI and SoC DAI must have their capabilities defined in order to -be configured together at runtime when the audio and clocking parameters are -known. This is achieved by creating an array of struct snd_soc_hw_mode in the -the CODEC and SoC interface drivers. Each element in the array describes a DAI -mode and each mode is usually based upon the DAI system clock to sample rate -ratio (FS). - -i.e. 48k sample rate @ 256 FS = sytem clock of 12.288 MHz - 48000 * 256 = 12288000 - -The CPU and Codec DAI modes are then ANDed together at runtime to determine the -rutime DAI configuration for both the Codec and CPU. - -When creating a new codec or SoC DAI it's probably best to start of with a few -sample rates first and then test your interface. - -struct snd_soc_dai_mode is defined (in soc.h) as:- - -/* SoC DAI mode */ -struct snd_soc_dai_mode { - u16 fmt; /* SND_SOC_DAIFMT_* */ - u16 tdm; /* SND_SOC_HWTDM_* */ - u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */ - u16 pcmrate; /* SND_SOC_HWRATE_* */ - u16 pcmdir:2; /* SND_SOC_HWDIR_* */ - u16 flags:8; /* hw flags */ - u16 fs; /* mclk to rate divider */ - u64 bfs; /* mclk to bclk dividers */ - unsigned long priv; /* private mode data */ -}; - -fmt: ----- -This field defines the DAI mode hardware format (e.g. I2S settings) and -supports the following settings:- - - 1) hardware DAI formats - -#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */ -#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */ -#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */ -#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM */ -#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM */ -#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */ - - 2) hw DAI signal inversions - -#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ -#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */ -#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */ -#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */ - - 3) hw clock masters - This is wrt the codec, the inverse is true for the interface - i.e. if the codec is clk and frm master then the interface is - clk and frame slave. - -#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */ -#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */ -#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */ -#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */ - -At least one option from each section must be selected. Multiple selections are -also supported e.g. - - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \ - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \ - SND_SOC_DAIFMT_IB_IF - - -tdm: ------- -This field defines the Time Division Multiplexing left and right word -positions for the DAI mode if applicable. Set to SND_SOC_DAITDM_LRDW(0,0) for -no TDM. - - -pcmfmt: ---------- -The hardware PCM format. This describes the PCM formats supported by the DAI -mode e.g. - - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_3LE - -pcmrate: ----------- -The PCM sample rates supported by the DAI mode. e.g. - - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 - - -pcmdir: ---------- -The stream directions supported by this mode. e.g. playback and capture - - -flags: --------- -The DAI hardware flags supported by the mode. - -/* use bfs mclk divider mode (BCLK = MCLK / x) */ -#define SND_SOC_DAI_BFS_DIV 0x1 -/* use bfs rate mulitplier (BCLK = RATE * x)*/ -#define SND_SOC_DAI_BFS_RATE 0x2 -/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */ -#define SND_SOC_DAI_BFS_RCW 0x4 -/* capture and playback can use different clocks */ -#define SND_SOC_DAI_ASYNC 0x8 - -NOTE: Bitclock division and mulitiplication modes can be safely matched by the -core logic. - - -fs: ------ -The FS supported by this DAI mode FS is the ratio between the system clock and -the sample rate. See above - -bfs: ------- -BFS is the ratio of BCLK to MCLK or the ratio of BCLK to sample rate (this -depends on the codec or CPU DAI). - -The BFS supported by the DAI mode. This can either be the ratio between the -bitclock (BCLK) and the sample rate OR the ratio between the system clock and -the sample rate. Depends on the flags above. - -priv: ------ -private codec mode data. - - - -Examples -======== - -Note that Codec DAI and CPU DAI examples are interchangeable in these examples -as long as the bus master is reversed. i.e. - - SND_SOC_DAIFMT_CBM_CFM would become SND_SOC_DAIFMT_CBS_CFS - and vice versa. - -This applies to all SND_SOC_DAIFMT_CB*_CF*. - -Example 1 ---------- - -Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a -BCLK of either MCLK/2 or MCLK/4. - - /* codec master */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(2) | SND_SOC_FSBD(4), - } - - -Example 2 ---------- -Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a -BCLK of either Rate * 32 or Rate * 64. - - /* codec master */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 256, - .bfs = 32, - }, - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 256, - .bfs = 64, - }, - - -Example 3 ---------- -Codec that runs at 8k & 48k @ 256FS in master mode, can generate a BCLK that -is a multiple of Rate * channels * word size. (RCW) i.e. - - BCLK = 8000 * 2 * 16 (8k, stereo, 16bit) - = 256kHz - -This codecs supports a RCW multiple of 1,2 - - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RCW, - .fs = 256, - .bfs = SND_SOC_FSBW(1) | SND_SOC_FSBW(2), - } - - -Example 4 ---------- -Codec that only runs at 8k & 48k @ 256FS in master mode, can generate a -BCLK of either Rate * 32 or Rate * 64. Codec can also run in slave mode as long -as BCLK is rate * 32 or rate * 64. - - /* codec master */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 256, - .bfs = 32, - }, - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = 256, - .bfs = 64, - }, - - /* codec slave */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = SND_SOC_FS_ALL, - .bfs = 32, - }, - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_RATE, - .fs = SND_SOC_FS_ALL, - .bfs = 64, - }, - - -Example 5 ---------- -Codec that only runs at 8k, 16k, 32k, 48k, 96k @ 128FS, 192FS & 256FS in master -mode and can generate a BCLK of MCLK / (1,2,4,8,16). Codec can also run in slave -mode as and does not care about FS or BCLK (as long as there is enough bandwidth). - - #define CODEC_FSB \ - (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ - SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) - - #define CODEC_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) - - /* codec master @ 128, 192 & 256 FS */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = CODEC_RATES, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 128, - .bfs = CODEC_FSB, - }, - - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = CODEC_RATES, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 192, - .bfs = CODEC_FSB - }, - - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = CODEC_RATES, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = CODEC_FSB, - }, - - /* codec slave */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = CODEC_RATES, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB_ALL, - }, - - -Example 6 ---------- -Codec that only runs at 8k, 44.1k, 48k @ different FS in master mode (for use -with a fixed MCLK) and can generate a BCLK of MCLK / (1,2,4,8,16). -Codec can also run in slave mode as and does not care about FS or BCLK (as long -as there is enough bandwidth). Codec can support 16, 24 and 32 bit PCM sample -sizes. - - #define CODEC_FSB \ - (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \ - SND_SOC_FSBD(8) | SND_SOC_FSBD(16)) - - #define CODEC_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE) - - /* codec master */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 1536, - .bfs = CODEC_FSB, - }, - - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 272, - .bfs = CODEC_FSB, - }, - - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = CODEC_FSB, - }, - - /* codec slave */ - { - .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FORMAT_S16_LE, - .pcmrate = CODEC_RATES, - .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE, - .fs = SND_SOC_FS_ALL, - .bfs = SND_SOC_FSB_ALL, - }, - - -Example 7 ---------- -AC97 Codec that does not support VRA (i.e only runs at 48k). - - #define AC97_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - - #define AC97_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \ - SNDRV_PCM_FORMAT_S20_3LE) - - /* AC97 with no VRA */ - { - .pcmfmt = AC97_PCM_FORMATS, - .pcmrate = SNDRV_PCM_RATE_48000, - } - - -Example 8 ---------- - -CPU DAI that supports 8k - 48k @ 256FS and BCLK = MCLK / 4 in master mode. -Slave mode (CPU DAI is FRAME master) supports 8k - 96k at any FS as long as -BCLK = 64 * rate. (Intel XScale I2S controller). - - #define PXA_I2S_DAIFMT \ - (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF) - - #define PXA_I2S_DIR \ - (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) - - #define PXA_I2S_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - - /* priv is divider */ - static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { - /* pxa2xx I2S frame and clock master modes */ - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_8000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x48, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_11025, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x34, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_16000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x24, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_22050, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0x1a, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_44100, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0xd, - }, - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = SNDRV_PCM_RATE_48000, - .pcmdir = PXA_I2S_DIR, - .flags = SND_SOC_DAI_BFS_DIV, - .fs = 256, - .bfs = SND_SOC_FSBD(4), - .priv = 0xc, - }, - - /* pxa2xx I2S frame master and clock slave mode */ - { - .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, - .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, - .pcmrate = PXA_I2S_RATES, - .pcmdir = PXA_I2S_DIR, - .fs = SND_SOC_FS_ALL, - .flags = SND_SOC_DAI_BFS_RATE, - .bfs = 64, - .priv = 0x48, - }, -}; diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/alsa/soc/clocking.txt index 1f55fd8cb117..e93960d53a1e 100644 --- a/Documentation/sound/alsa/soc/clocking.txt +++ b/Documentation/sound/alsa/soc/clocking.txt @@ -40,275 +40,12 @@ BCLK = LRC * x BCLK = LRC * Channels * Word Size -This relationship depends on the codec or SoC CPU in particular. ASoC can quite -easily match BCLK generated by division (SND_SOC_DAI_BFS_DIV) with BCLK by -multiplication (SND_SOC_DAI_BFS_RATE) or BCLK generated by -Rate * Channels * Word size (RCW or SND_SOC_DAI_BFS_RCW). +This relationship depends on the codec or SoC CPU in particular. In general +it's best to configure BCLK to the lowest possible speed (depending on your +rate, number of channels and wordsize) to save on power. +It's also desireable to use the codec (if possible) to drive (or master) the +audio clocks as it's usually gives more accurate sample rates than the CPU. -ASoC Clocking -------------- -The ASoC core determines the clocking for each particular configuration at -runtime. This is to allow for dynamic audio clocking wereby the audio clock is -variable and depends on the system state or device usage scenario. i.e. a voice -call requires slower clocks (and hence less power) than MP3 playback. -ASoC will call the config_sysclock() function for the target machine during the -audio parameters configuration. The function is responsible for then clocking -the machine audio subsytem and returning the audio clock speed to the core. -This function should also call the codec and cpu DAI clock_config() functions -to configure their respective internal clocking if required. - - -ASoC Clocking Control Flow --------------------------- - -The ASoC core will call the machine drivers config_sysclock() when most of the -DAI capabilities are known. The machine driver is then responsible for calling -the codec and/or CPU DAI drivers with the selected capabilities and the current -MCLK. Note that the machine driver is also resonsible for setting the MCLK (and -enabling it). - - (1) Match Codec and CPU DAI capabilities. At this point we have - matched the majority of the DAI fields and now need to make sure this - mode is currently clockable. - - (2) machine->config_sysclk() is now called with the matched DAI FS, sample - rate and BCLK master. This function then gets/sets the current audio - clock (depening on usage) and calls the codec and CPUI DAI drivers with - the FS, rate, BCLK master and MCLK. - - (3) Codec/CPU DAI config_sysclock(). This function checks that the FS, rate, - BCLK master and MCLK are acceptable for the codec or CPU DAI. It also - sets the DAI internal state to work with said clocks. - -The config_sysclk() functions for CPU, codec and machine should return the MCLK -on success and 0 on failure. - - -Examples (b = BCLK, l = LRC) -============================ - -Example 1 ---------- - -Simple codec that only runs at 48k @ 256FS in master mode. - -CPU only runs as slave DAI, however it generates a variable MCLK. - - -------- --------- - | | <----mclk--- | | - | Codec |b -----------> | CPU | - | |l -----------> | | - | | | | - -------- --------- - -The codec driver has the following config_sysclock() - - static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) - { - /* make sure clock is 256 * rate */ - if(info->rate << 8 == clk) { - dai->mclk = clk; - return clk; - } - - return 0; - } - -The CPU I2S DAI driver has the following config_sysclk() - - static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) - { - /* can we support this clk */ - if(set_audio_clk(clk) < 0) - return -EINVAL; - - dai->mclk = clk; - return dai->clk; - } - -The machine driver config_sysclk() in this example is as follows:- - - unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) - { - int clk = info->rate * info->fs; - - /* check that CPU can deliver clock */ - if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0) - return -EINVAL; - - /* can codec work with this clock */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk); - } - - -Example 2 ---------- - -Codec that can master at 8k and 48k at various FS (and hence supports a fixed -set of input MCLK's) and can also be slave at various FS . - -The CPU can master at 8k and 48k @256 FS and can be slave at any FS. - -MCLK is a 12.288MHz crystal on this machine. - - -------- --------- - | | <---xtal---> | | - | Codec |b <----------> | CPU | - | |l <----------> | | - | | | | - -------- --------- - - -The codec driver has the following config_sysclock() - - /* supported input clocks */ - const static int hifi_clks[] = {11289600, 12000000, 12288000, - 16934400, 18432000}; - - static unsigned int config_hsysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) - { - int i; - - /* is clk supported */ - for(i = 0; i < ARRAY_SIZE(hifi_clks); i++) { - if(clk == hifi_clks[i]) { - dai->mclk = clk; - return clk; - } - } - - /* this clk is not supported */ - return 0; - } - -The CPU I2S DAI driver has the following config_sysclk() - - static unsigned int config_sysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) - { - /* are we master or slave */ - if (info->bclk_master & - (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) { - - /* we can only master @ 256FS */ - if(info->rate << 8 == clk) { - dai->mclk = clk; - return dai->mclk; - } - } else { - /* slave we can run at any FS */ - dai->mclk = clk; - return dai->mclk; - } - - /* not supported */ - return dai->clk; - } - -The machine driver config_sysclk() in this example is as follows:- - - unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) - { - int clk = 12288000; /* 12.288MHz */ - - /* who's driving the link */ - if (info->bclk_master & - (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) { - /* codec master */ - - /* check that CPU can work with clock */ - if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0) - return -EINVAL; - - /* can codec work with this clock */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk); - } else { - /* cpu master */ - - /* check that codec can work with clock */ - if(rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk) < 0) - return -EINVAL; - - /* can CPU work with this clock */ - return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk); - } - } - - - -Example 3 ---------- - -Codec that masters at 8k ... 48k @256 FS. Codec can also be slave and -doesn't care about FS. The codec has an internal PLL and dividers to generate -the necessary internal clocks (for 256FS). - -CPU can only be slave and doesn't care about FS. - -MCLK is a non controllable 13MHz clock from the CPU. - - - -------- --------- - | | <----mclk--- | | - | Codec |b <----------> | CPU | - | |l <----------> | | - | | | | - -------- --------- - -The codec driver has the following config_sysclock() - - /* valid PCM clock dividers * 2 */ - static int pcm_divs[] = {2, 6, 11, 4, 8, 12, 16}; - - static unsigned int config_vsysclk(struct snd_soc_codec_dai *dai, - struct snd_soc_clock_info *info, unsigned int clk) - { - int i, j, best_clk = info->fs * info->rate; - - /* can we run at this clk without the PLL ? */ - for (i = 0; i < ARRAY_SIZE(pcm_divs); i++) { - if ((best_clk >> 1) * pcm_divs[i] == clk) { - dai->pll_in = 0; - dai->clk_div = pcm_divs[i]; - dai->mclk = best_clk; - return dai->mclk; - } - } - - /* now check for PLL support */ - for (i = 0; i < ARRAY_SIZE(pll_div); i++) { - if (pll_div[i].pll_in == clk) { - for (j = 0; j < ARRAY_SIZE(pcm_divs); j++) { - if (pll_div[i].pll_out == pcm_divs[j] * (best_clk >> 1)) { - dai->pll_in = clk; - dai->pll_out = pll_div[i].pll_out; - dai->clk_div = pcm_divs[j]; - dai->mclk = best_clk; - return dai->mclk; - } - } - } - } - - /* this clk is not supported */ - return 0; - } - - -The CPU I2S DAI driver has the does not need a config_sysclk() as it can slave -at any FS. - - unsigned int config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) - { - /* codec has pll that generates mclk from 13MHz xtal */ - return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000); - } diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt index 274657a03e1c..48983c75aad9 100644 --- a/Documentation/sound/alsa/soc/codec.txt +++ b/Documentation/sound/alsa/soc/codec.txt @@ -6,21 +6,18 @@ codec to provide audio capture and playback. It should contain no code that is specific to the target platform or machine. All platform and machine specific code should be added to the platform and machine drivers respectively. -Each codec driver must provide the following features:- +Each codec driver *must* provide the following features:- - 1) Digital audio interface (DAI) description - 2) Digital audio interface configuration - 3) PCM's description - 4) Codec control IO - using I2C, 3 Wire(SPI) or both API's - 5) Mixers and audio controls - 6) Sysclk configuration - 7) Codec audio operations + 1) Codec DAI and PCM configuration + 2) Codec control IO - using I2C, 3 Wire(SPI) or both API's + 3) Mixers and audio controls + 4) Codec audio operations Optionally, codec drivers can also provide:- - 8) DAPM description. - 9) DAPM event handler. -10) DAC Digital mute control. + 5) DAPM description. + 6) DAPM event handler. + 7) DAC Digital mute control. It's probably best to use this guide in conjuction with the existing codec driver code in sound/soc/codecs/ @@ -28,58 +25,47 @@ driver code in sound/soc/codecs/ ASoC Codec driver breakdown =========================== -1 - Digital Audio Interface (DAI) description ---------------------------------------------- -The DAI is a digital audio data transfer link between the codec and host SoC -CPU. It typically has data transfer capabilities in both directions -(playback and capture) and can run at a variety of different speeds. -Supported interfaces currently include AC97, I2S and generic PCM style links. -Please read DAI.txt for implementation information. - - -2 - Digital Audio Interface (DAI) configuration ------------------------------------------------ -DAI configuration is handled by the codec_pcm_prepare function and is -responsible for configuring and starting the DAI on the codec. This can be -called multiple times and is atomic. It can access the runtime parameters. - -This usually consists of a large function with numerous switch statements to -set up each configuration option. These options are set by the core at runtime. - - -3 - Codec PCM's ---------------- -Each codec must have it's PCM's defined. This defines the number of channels, -stream names, callbacks and codec name. It is also used to register the DAI -with the ASoC core. The PCM structure also associates the DAI capabilities with -the ALSA PCM. +1 - Codec DAI and PCM configuration +----------------------------------- +Each codec driver must have a struct snd_soc_codec_dai to define it's DAI and +PCM's capablities and operations. This struct is exported so that it can be +registered with the core by your machine driver. e.g. -static struct snd_soc_pcm_codec wm8731_pcm_client = { +struct snd_soc_codec_dai wm8731_dai = { .name = "WM8731", + /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - }, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, + /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, - }, - .config_sysclk = wm8731_config_sysclk, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, + /* pcm operations - see section 4 below */ .ops = { .prepare = wm8731_pcm_prepare, + .hw_params = wm8731_hw_params, + .shutdown = wm8731_shutdown, }, - .caps = { - .num_modes = ARRAY_SIZE(wm8731_hwfmt), - .modes = &wm8731_hwfmt[0], - }, + /* DAI operations - see DAI.txt */ + .dai_ops = { + .digital_mute = wm8731_mute, + .set_sysclk = wm8731_set_dai_sysclk, + .set_fmt = wm8731_set_dai_fmt, + } }; +EXPORT_SYMBOL_GPL(wm8731_dai); -4 - Codec control IO +2 - Codec control IO -------------------- The codec can ususally be controlled via an I2C or SPI style interface (AC97 combines control with data in the DAI). The codec drivers will have to provide @@ -104,7 +90,7 @@ read/write:- hw_read_t hw_read; -5 - Mixers and audio controls +3 - Mixers and audio controls ----------------------------- All the codec mixers and audio controls can be defined using the convenience macros defined in soc.h. @@ -143,28 +129,7 @@ Defines an single enumerated control as follows:- Defines a stereo enumerated control -6 - System clock configuration. -------------------------------- -The system clock that drives the audio subsystem can change depending on sample -rate and the system power state. i.e. - -o Higher sample rates sometimes need a higher system clock. -o Low system power states can sometimes limit the available clocks. - -This function is a callback that the machine driver can call to set and -determine if the clock and sample rate combination is supported by the codec at -the present time (and system state). - -NOTE: If the codec has a PLL then it has a lot more flexability wrt clock and -sample rate combinations. - -Your config_sysclock function should return the MCLK if it's a valid -combination for your codec else 0; - -Please read clocking.txt now. - - -7 - Codec Audio Operations +4 - Codec Audio Operations -------------------------- The codec driver also supports the following alsa operations:- @@ -181,7 +146,7 @@ Please refer to the alsa driver PCM documentation for details. http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm -8 - DAPM description. +5 - DAPM description. --------------------- The Dynamic Audio Power Management description describes the codec's power components, their relationships and registers to the ASoC core. Please read @@ -190,7 +155,7 @@ dapm.txt for details of building the description. Please also see the examples in other codec drivers. -9 - DAPM event handler +6 - DAPM event handler ---------------------- This function is a callback that handles codec domain PM calls and system domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep @@ -210,7 +175,7 @@ Power states:- SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */ -10 - Codec DAC digital mute control. +7 - Codec DAC digital mute control. ------------------------------------ Most codecs have a digital mute before the DAC's that can be used to minimise any system noise. The mute stops any digital data from entering the DAC. diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt index 3014795b1733..72bd222f2a21 100644 --- a/Documentation/sound/alsa/soc/machine.txt +++ b/Documentation/sound/alsa/soc/machine.txt @@ -64,7 +64,7 @@ static struct snd_soc_dai_link corgi_dai = { .cpu_dai = &pxa_i2s_dai, .codec_dai = &wm8731_dai, .init = corgi_wm8731_init, - .config_sysclk = corgi_config_sysclk, + .ops = &corgi_ops, }; struct snd_soc_machine then sets up the machine with it's DAI's. e.g. @@ -74,7 +74,6 @@ static struct snd_soc_machine snd_soc_machine_corgi = { .name = "Corgi", .dai_link = &corgi_dai, .num_links = 1, - .ops = &corgi_ops, }; diff --git a/Documentation/sound/alsa/soc/pops_clicks.txt b/Documentation/sound/alsa/soc/pops_clicks.txt index f4f8de5a9686..2cf7ee5b3d74 100644 --- a/Documentation/sound/alsa/soc/pops_clicks.txt +++ b/Documentation/sound/alsa/soc/pops_clicks.txt @@ -2,14 +2,14 @@ Audio Pops and Clicks ===================== Pops and clicks are unwanted audio artifacts caused by the powering up and down -of components within the audio subsystem. This is noticable on PC's when an audio -module is either loaded or unloaded (at module load time the sound card is +of components within the audio subsystem. This is noticable on PC's when an +audio module is either loaded or unloaded (at module load time the sound card is powered up and causes a popping noise on the speakers). -Pops and clicks can be more frequent on portable systems with DAPM. This is because -the components within the subsystem are being dynamically powered depending on -the audio usage and this can subsequently cause a small pop or click every time a -component power state is changed. +Pops and clicks can be more frequent on portable systems with DAPM. This is +because the components within the subsystem are being dynamically powered +depending on the audio usage and this can subsequently cause a small pop or +click every time a component power state is changed. Minimising Playback Pops and Clicks -- cgit v1.2.3-59-g8ed1b From 48ec15dca87805cf771855612d647bfe1a9f617f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 9 Feb 2007 14:50:18 +0100 Subject: [ALSA] version 1.0.14rc2 Signed-off-by: Jaroslav Kysela --- include/sound/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/version.h b/include/sound/version.h index 20f7babad514..c39b3802cf18 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated by alsa/ksync script. */ -#define CONFIG_SND_VERSION "1.0.14rc1" -#define CONFIG_SND_DATE " (Tue Jan 09 09:56:17 2007 UTC)" +#define CONFIG_SND_VERSION "1.0.14rc2" +#define CONFIG_SND_DATE " (Fri Feb 09 13:50:10 2007 UTC)" -- cgit v1.2.3-59-g8ed1b From c2902c8ae06762d941fab64198467f78cab6f8cd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 9 Feb 2007 16:25:48 +0100 Subject: [PATCH] Fix breakage with CONFIG_SYSFS_DEPRECATED The fix for sysfs breakage with CONFIG_SYSFS_DEPRECATED was flown away by the conflicted merge of the ALSA git tree. The patch below fixes it again. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/core.h | 2 +- sound/core/pcm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/core.h b/include/sound/core.h index 3c493ad620d1..4b9e609975ab 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -242,7 +242,7 @@ static inline int snd_register_device(int type, struct snd_card *card, int dev, { return snd_register_device_for_dev(type, card, dev, f_ops, private_data, name, - card ? card->dev : NULL); + snd_card_get_device_link(card)); } int snd_unregister_device(int type, struct snd_card *card, int dev); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 76fcc5234d83..2743414fc8fa 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -968,7 +968,7 @@ static int snd_pcm_dev_register(struct snd_device *device) * if possible */ dev = pcm->dev; if (!dev) - dev = pcm->card ? pcm->card->dev : NULL; + dev = snd_card_get_device_link(pcm->card); /* register pcm */ err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, -- cgit v1.2.3-59-g8ed1b