Merge branch 'topic/hda' into for-linus
This commit is contained in:
commit
86e1d57e4f
|
@ -798,6 +798,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
setup before initializing the codecs. This option is
|
||||
available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
|
||||
See HD-Audio.txt for details.
|
||||
beep_mode - Selects the beep registration mode (0=off, 1=on, 2=
|
||||
dynamic registration via mute switch on/off); the default
|
||||
value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig.
|
||||
|
||||
[Single (global) options]
|
||||
single_cmd - Use single immediate commands to communicate with
|
||||
|
|
|
@ -391,6 +391,7 @@ STAC92HD83*
|
|||
ref Reference board
|
||||
mic-ref Reference board with power management for ports
|
||||
dell-s14 Dell laptop
|
||||
hp HP laptops with (inverted) mute-LED
|
||||
auto BIOS setup (default)
|
||||
|
||||
STAC9872
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
#ifndef __SOUND_AK4113_H
|
||||
#define __SOUND_AK4113_H
|
||||
|
||||
/*
|
||||
* Routines for Asahi Kasei AK4113
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
|
||||
* Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* AK4113 registers */
|
||||
/* power down */
|
||||
#define AK4113_REG_PWRDN 0x00
|
||||
/* format control */
|
||||
#define AK4113_REG_FORMAT 0x01
|
||||
/* input/output control */
|
||||
#define AK4113_REG_IO0 0x02
|
||||
/* input/output control */
|
||||
#define AK4113_REG_IO1 0x03
|
||||
/* interrupt0 mask */
|
||||
#define AK4113_REG_INT0_MASK 0x04
|
||||
/* interrupt1 mask */
|
||||
#define AK4113_REG_INT1_MASK 0x05
|
||||
/* DAT mask & DTS select */
|
||||
#define AK4113_REG_DATDTS 0x06
|
||||
/* receiver status 0 */
|
||||
#define AK4113_REG_RCS0 0x07
|
||||
/* receiver status 1 */
|
||||
#define AK4113_REG_RCS1 0x08
|
||||
/* receiver status 2 */
|
||||
#define AK4113_REG_RCS2 0x09
|
||||
/* RX channel status byte 0 */
|
||||
#define AK4113_REG_RXCSB0 0x0a
|
||||
/* RX channel status byte 1 */
|
||||
#define AK4113_REG_RXCSB1 0x0b
|
||||
/* RX channel status byte 2 */
|
||||
#define AK4113_REG_RXCSB2 0x0c
|
||||
/* RX channel status byte 3 */
|
||||
#define AK4113_REG_RXCSB3 0x0d
|
||||
/* RX channel status byte 4 */
|
||||
#define AK4113_REG_RXCSB4 0x0e
|
||||
/* burst preamble Pc byte 0 */
|
||||
#define AK4113_REG_Pc0 0x0f
|
||||
/* burst preamble Pc byte 1 */
|
||||
#define AK4113_REG_Pc1 0x10
|
||||
/* burst preamble Pd byte 0 */
|
||||
#define AK4113_REG_Pd0 0x11
|
||||
/* burst preamble Pd byte 1 */
|
||||
#define AK4113_REG_Pd1 0x12
|
||||
/* Q-subcode address + control */
|
||||
#define AK4113_REG_QSUB_ADDR 0x13
|
||||
/* Q-subcode track */
|
||||
#define AK4113_REG_QSUB_TRACK 0x14
|
||||
/* Q-subcode index */
|
||||
#define AK4113_REG_QSUB_INDEX 0x15
|
||||
/* Q-subcode minute */
|
||||
#define AK4113_REG_QSUB_MINUTE 0x16
|
||||
/* Q-subcode second */
|
||||
#define AK4113_REG_QSUB_SECOND 0x17
|
||||
/* Q-subcode frame */
|
||||
#define AK4113_REG_QSUB_FRAME 0x18
|
||||
/* Q-subcode zero */
|
||||
#define AK4113_REG_QSUB_ZERO 0x19
|
||||
/* Q-subcode absolute minute */
|
||||
#define AK4113_REG_QSUB_ABSMIN 0x1a
|
||||
/* Q-subcode absolute second */
|
||||
#define AK4113_REG_QSUB_ABSSEC 0x1b
|
||||
/* Q-subcode absolute frame */
|
||||
#define AK4113_REG_QSUB_ABSFRM 0x1c
|
||||
|
||||
/* sizes */
|
||||
#define AK4113_REG_RXCSB_SIZE ((AK4113_REG_RXCSB4-AK4113_REG_RXCSB0)+1)
|
||||
#define AK4113_REG_QSUB_SIZE ((AK4113_REG_QSUB_ABSFRM-AK4113_REG_QSUB_ADDR)\
|
||||
+1)
|
||||
|
||||
#define AK4113_WRITABLE_REGS (AK4113_REG_DATDTS + 1)
|
||||
|
||||
/* AK4113_REG_PWRDN bits */
|
||||
/* Channel Status Select */
|
||||
#define AK4113_CS12 (1<<7)
|
||||
/* Block Start & C/U Output Mode */
|
||||
#define AK4113_BCU (1<<6)
|
||||
/* Master Clock Operation Select */
|
||||
#define AK4113_CM1 (1<<5)
|
||||
/* Master Clock Operation Select */
|
||||
#define AK4113_CM0 (1<<4)
|
||||
/* Master Clock Frequency Select */
|
||||
#define AK4113_OCKS1 (1<<3)
|
||||
/* Master Clock Frequency Select */
|
||||
#define AK4113_OCKS0 (1<<2)
|
||||
/* 0 = power down, 1 = normal operation */
|
||||
#define AK4113_PWN (1<<1)
|
||||
/* 0 = reset & initialize (except thisregister), 1 = normal operation */
|
||||
#define AK4113_RST (1<<0)
|
||||
|
||||
/* AK4113_REQ_FORMAT bits */
|
||||
/* V/TX Output select: 0 = Validity Flag Output, 1 = TX */
|
||||
#define AK4113_VTX (1<<7)
|
||||
/* Audio Data Control */
|
||||
#define AK4113_DIF2 (1<<6)
|
||||
/* Audio Data Control */
|
||||
#define AK4113_DIF1 (1<<5)
|
||||
/* Audio Data Control */
|
||||
#define AK4113_DIF0 (1<<4)
|
||||
/* Deemphasis Autodetect Enable (1 = enable) */
|
||||
#define AK4113_DEAU (1<<3)
|
||||
/* 32kHz-48kHz Deemphasis Control */
|
||||
#define AK4113_DEM1 (1<<2)
|
||||
/* 32kHz-48kHz Deemphasis Control */
|
||||
#define AK4113_DEM0 (1<<1)
|
||||
#define AK4113_DEM_OFF (AK4113_DEM0)
|
||||
#define AK4113_DEM_44KHZ (0)
|
||||
#define AK4113_DEM_48KHZ (AK4113_DEM1)
|
||||
#define AK4113_DEM_32KHZ (AK4113_DEM0|AK4113_DEM1)
|
||||
/* STDO: 16-bit, right justified */
|
||||
#define AK4113_DIF_16R (0)
|
||||
/* STDO: 18-bit, right justified */
|
||||
#define AK4113_DIF_18R (AK4113_DIF0)
|
||||
/* STDO: 20-bit, right justified */
|
||||
#define AK4113_DIF_20R (AK4113_DIF1)
|
||||
/* STDO: 24-bit, right justified */
|
||||
#define AK4113_DIF_24R (AK4113_DIF1|AK4113_DIF0)
|
||||
/* STDO: 24-bit, left justified */
|
||||
#define AK4113_DIF_24L (AK4113_DIF2)
|
||||
/* STDO: I2S */
|
||||
#define AK4113_DIF_24I2S (AK4113_DIF2|AK4113_DIF0)
|
||||
/* STDO: 24-bit, left justified; LRCLK, BICK = Input */
|
||||
#define AK4113_DIF_I24L (AK4113_DIF2|AK4113_DIF1)
|
||||
/* STDO: I2S; LRCLK, BICK = Input */
|
||||
#define AK4113_DIF_I24I2S (AK4113_DIF2|AK4113_DIF1|AK4113_DIF0)
|
||||
|
||||
/* AK4113_REG_IO0 */
|
||||
/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */
|
||||
#define AK4113_XTL1 (1<<6)
|
||||
/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */
|
||||
#define AK4113_XTL0 (1<<5)
|
||||
/* Block Start Signal Output: 0 = U-bit, 1 = C-bit (req. BCU = 1) */
|
||||
#define AK4113_UCE (1<<4)
|
||||
/* TX Output Enable (1 = enable) */
|
||||
#define AK4113_TXE (1<<3)
|
||||
/* Output Through Data Selector for TX pin */
|
||||
#define AK4113_OPS2 (1<<2)
|
||||
/* Output Through Data Selector for TX pin */
|
||||
#define AK4113_OPS1 (1<<1)
|
||||
/* Output Through Data Selector for TX pin */
|
||||
#define AK4113_OPS0 (1<<0)
|
||||
/* 11.2896 MHz ref. Xtal freq. */
|
||||
#define AK4113_XTL_11_2896M (0)
|
||||
/* 12.288 MHz ref. Xtal freq. */
|
||||
#define AK4113_XTL_12_288M (AK4113_XTL0)
|
||||
/* 24.576 MHz ref. Xtal freq. */
|
||||
#define AK4113_XTL_24_576M (AK4113_XTL1)
|
||||
|
||||
/* AK4113_REG_IO1 */
|
||||
/* Interrupt 0 pin Hold */
|
||||
#define AK4113_EFH1 (1<<7)
|
||||
/* Interrupt 0 pin Hold */
|
||||
#define AK4113_EFH0 (1<<6)
|
||||
#define AK4113_EFH_512LRCLK (0)
|
||||
#define AK4113_EFH_1024LRCLK (AK4113_EFH0)
|
||||
#define AK4113_EFH_2048LRCLK (AK4113_EFH1)
|
||||
#define AK4113_EFH_4096LRCLK (AK4113_EFH1|AK4113_EFH0)
|
||||
/* PLL Lock Time: 0 = 384/fs, 1 = 1/fs */
|
||||
#define AK4113_FAST (1<<5)
|
||||
/* MCKO2 Output Select: 0 = CMx/OCKSx, 1 = Xtal */
|
||||
#define AK4113_XMCK (1<<4)
|
||||
/* MCKO2 Output Freq. Select: 0 = x1, 1 = x0.5 (req. XMCK = 1) */
|
||||
#define AK4113_DIV (1<<3)
|
||||
/* Input Recovery Data Select */
|
||||
#define AK4113_IPS2 (1<<2)
|
||||
/* Input Recovery Data Select */
|
||||
#define AK4113_IPS1 (1<<1)
|
||||
/* Input Recovery Data Select */
|
||||
#define AK4113_IPS0 (1<<0)
|
||||
#define AK4113_IPS(x) ((x)&7)
|
||||
|
||||
/* AK4113_REG_INT0_MASK && AK4113_REG_INT1_MASK*/
|
||||
/* mask enable for QINT bit */
|
||||
#define AK4113_MQI (1<<7)
|
||||
/* mask enable for AUTO bit */
|
||||
#define AK4113_MAUT (1<<6)
|
||||
/* mask enable for CINT bit */
|
||||
#define AK4113_MCIT (1<<5)
|
||||
/* mask enable for UNLOCK bit */
|
||||
#define AK4113_MULK (1<<4)
|
||||
/* mask enable for V bit */
|
||||
#define AK4113_V (1<<3)
|
||||
/* mask enable for STC bit */
|
||||
#define AK4113_STC (1<<2)
|
||||
/* mask enable for AUDN bit */
|
||||
#define AK4113_MAN (1<<1)
|
||||
/* mask enable for PAR bit */
|
||||
#define AK4113_MPR (1<<0)
|
||||
|
||||
/* AK4113_REG_DATDTS */
|
||||
/* DAT Start ID Counter */
|
||||
#define AK4113_DCNT (1<<4)
|
||||
/* DTS-CD 16-bit Sync Word Detect */
|
||||
#define AK4113_DTS16 (1<<3)
|
||||
/* DTS-CD 14-bit Sync Word Detect */
|
||||
#define AK4113_DTS14 (1<<2)
|
||||
/* mask enable for DAT bit (if 1, no INT1 effect */
|
||||
#define AK4113_MDAT1 (1<<1)
|
||||
/* mask enable for DAT bit (if 1, no INT0 effect */
|
||||
#define AK4113_MDAT0 (1<<0)
|
||||
|
||||
/* AK4113_REG_RCS0 */
|
||||
/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
|
||||
#define AK4113_QINT (1<<7)
|
||||
/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
|
||||
#define AK4113_AUTO (1<<6)
|
||||
/* channel status buffer interrupt, 0 = no change, 1 = change */
|
||||
#define AK4113_CINT (1<<5)
|
||||
/* PLL lock status, 0 = lock, 1 = unlock */
|
||||
#define AK4113_UNLCK (1<<4)
|
||||
/* Validity bit, 0 = valid, 1 = invalid */
|
||||
#define AK4113_V (1<<3)
|
||||
/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */
|
||||
#define AK4113_STC (1<<2)
|
||||
/* audio bit output, 0 = audio, 1 = non-audio */
|
||||
#define AK4113_AUDION (1<<1)
|
||||
/* parity error or biphase error status, 0 = no error, 1 = error */
|
||||
#define AK4113_PAR (1<<0)
|
||||
|
||||
/* AK4113_REG_RCS1 */
|
||||
/* sampling frequency detection */
|
||||
#define AK4113_FS3 (1<<7)
|
||||
#define AK4113_FS2 (1<<6)
|
||||
#define AK4113_FS1 (1<<5)
|
||||
#define AK4113_FS0 (1<<4)
|
||||
/* Pre-emphasis detect, 0 = OFF, 1 = ON */
|
||||
#define AK4113_PEM (1<<3)
|
||||
/* DAT Start ID Detect, 0 = no detect, 1 = detect */
|
||||
#define AK4113_DAT (1<<2)
|
||||
/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */
|
||||
#define AK4113_DTSCD (1<<1)
|
||||
/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */
|
||||
#define AK4113_NPCM (1<<0)
|
||||
#define AK4113_FS_8000HZ (AK4113_FS3|AK4113_FS0)
|
||||
#define AK4113_FS_11025HZ (AK4113_FS2|AK4113_FS0)
|
||||
#define AK4113_FS_16000HZ (AK4113_FS2|AK4113_FS1|AK4113_FS0)
|
||||
#define AK4113_FS_22050HZ (AK4113_FS2)
|
||||
#define AK4113_FS_24000HZ (AK4113_FS2|AK4113_FS1)
|
||||
#define AK4113_FS_32000HZ (AK4113_FS1|AK4113_FS0)
|
||||
#define AK4113_FS_44100HZ (0)
|
||||
#define AK4113_FS_48000HZ (AK4113_FS1)
|
||||
#define AK4113_FS_64000HZ (AK4113_FS3|AK4113_FS1|AK4113_FS0)
|
||||
#define AK4113_FS_88200HZ (AK4113_FS3)
|
||||
#define AK4113_FS_96000HZ (AK4113_FS3|AK4113_FS1)
|
||||
#define AK4113_FS_176400HZ (AK4113_FS3|AK4113_FS2)
|
||||
#define AK4113_FS_192000HZ (AK4113_FS3|AK4113_FS2|AK4113_FS1)
|
||||
|
||||
/* AK4113_REG_RCS2 */
|
||||
/* CRC for Q-subcode, 0 = no error, 1 = error */
|
||||
#define AK4113_QCRC (1<<1)
|
||||
/* CRC for channel status, 0 = no error, 1 = error */
|
||||
#define AK4113_CCRC (1<<0)
|
||||
|
||||
/* flags for snd_ak4113_check_rate_and_errors() */
|
||||
#define AK4113_CHECK_NO_STAT (1<<0) /* no statistics */
|
||||
#define AK4113_CHECK_NO_RATE (1<<1) /* no rate check */
|
||||
|
||||
#define AK4113_CONTROLS 13
|
||||
|
||||
typedef void (ak4113_write_t)(void *private_data, unsigned char addr,
|
||||
unsigned char data);
|
||||
typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr);
|
||||
|
||||
struct ak4113 {
|
||||
struct snd_card *card;
|
||||
ak4113_write_t *write;
|
||||
ak4113_read_t *read;
|
||||
void *private_data;
|
||||
unsigned int init:1;
|
||||
spinlock_t lock;
|
||||
unsigned char regmap[AK4113_WRITABLE_REGS];
|
||||
struct snd_kcontrol *kctls[AK4113_CONTROLS];
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned long parity_errors;
|
||||
unsigned long v_bit_errors;
|
||||
unsigned long qcrc_errors;
|
||||
unsigned long ccrc_errors;
|
||||
unsigned char rcs0;
|
||||
unsigned char rcs1;
|
||||
unsigned char rcs2;
|
||||
struct delayed_work work;
|
||||
unsigned int check_flags;
|
||||
void *change_callback_private;
|
||||
void (*change_callback)(struct ak4113 *ak4113, unsigned char c0,
|
||||
unsigned char c1);
|
||||
};
|
||||
|
||||
int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
|
||||
ak4113_write_t *write,
|
||||
const unsigned char pgm[AK4113_WRITABLE_REGS],
|
||||
void *private_data, struct ak4113 **r_ak4113);
|
||||
void snd_ak4113_reg_write(struct ak4113 *ak4113, unsigned char reg,
|
||||
unsigned char mask, unsigned char val);
|
||||
void snd_ak4113_reinit(struct ak4113 *ak4113);
|
||||
int snd_ak4113_build(struct ak4113 *ak4113,
|
||||
struct snd_pcm_substream *capture_substream);
|
||||
int snd_ak4113_external_rate(struct ak4113 *ak4113);
|
||||
int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags);
|
||||
|
||||
#endif /* __SOUND_AK4113_H */
|
||||
|
|
@ -95,13 +95,13 @@
|
|||
|
||||
/* AK4114_REG_IO0 */
|
||||
#define AK4114_TX1E (1<<7) /* TX1 Output Enable (1 = enable) */
|
||||
#define AK4114_OPS12 (1<<2) /* Output Though Data Selector for TX1 pin */
|
||||
#define AK4114_OPS11 (1<<1) /* Output Though Data Selector for TX1 pin */
|
||||
#define AK4114_OPS10 (1<<0) /* Output Though Data Selector for TX1 pin */
|
||||
#define AK4114_OPS12 (1<<6) /* Output Data Selector for TX1 pin */
|
||||
#define AK4114_OPS11 (1<<5) /* Output Data Selector for TX1 pin */
|
||||
#define AK4114_OPS10 (1<<4) /* Output Data Selector for TX1 pin */
|
||||
#define AK4114_TX0E (1<<3) /* TX0 Output Enable (1 = enable) */
|
||||
#define AK4114_OPS02 (1<<2) /* Output Though Data Selector for TX0 pin */
|
||||
#define AK4114_OPS01 (1<<1) /* Output Though Data Selector for TX0 pin */
|
||||
#define AK4114_OPS00 (1<<0) /* Output Though Data Selector for TX0 pin */
|
||||
#define AK4114_OPS02 (1<<2) /* Output Data Selector for TX0 pin */
|
||||
#define AK4114_OPS01 (1<<1) /* Output Data Selector for TX0 pin */
|
||||
#define AK4114_OPS00 (1<<0) /* Output Data Selector for TX0 pin */
|
||||
|
||||
/* AK4114_REG_IO1 */
|
||||
#define AK4114_EFH1 (1<<7) /* Interrupt 0 pin Hold */
|
||||
|
|
|
@ -68,7 +68,7 @@ struct snd_akm4xxx {
|
|||
enum {
|
||||
SND_AK4524, SND_AK4528, SND_AK4529,
|
||||
SND_AK4355, SND_AK4358, SND_AK4381,
|
||||
SND_AK5365
|
||||
SND_AK5365, SND_AK4620,
|
||||
} type;
|
||||
|
||||
/* (array) information of combined codecs */
|
||||
|
@ -76,6 +76,9 @@ struct snd_akm4xxx {
|
|||
const struct snd_akm4xxx_adc_channel *adc_info;
|
||||
|
||||
struct snd_ak4xxx_ops ops;
|
||||
unsigned int num_chips;
|
||||
unsigned int total_regs;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
snd-ak4114-objs := ak4114.o
|
||||
snd-ak4117-objs := ak4117.o
|
||||
snd-ak4113-objs := ak4113.o
|
||||
snd-ak4xxx-adda-objs := ak4xxx-adda.o
|
||||
snd-pt2258-objs := pt2258.o
|
||||
snd-tea575x-tuner-objs := tea575x-tuner.o
|
||||
|
@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
|
|||
# Module Dependency
|
||||
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
|
||||
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
|
||||
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
|
||||
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
|
||||
obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
|
||||
|
|
|
@ -0,0 +1,639 @@
|
|||
/*
|
||||
* Routines for control of the AK4113 via I2C/4-wire serial interface
|
||||
* IEC958 (S/PDIF) receiver by Asahi Kasei
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.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.
|
||||
*
|
||||
* 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 <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/ak4113.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
|
||||
MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define AK4113_ADDR 0x00 /* fixed address */
|
||||
|
||||
static void ak4113_stats(struct work_struct *work);
|
||||
static void ak4113_init_regs(struct ak4113 *chip);
|
||||
|
||||
|
||||
static void reg_write(struct ak4113 *ak4113, unsigned char reg,
|
||||
unsigned char val)
|
||||
{
|
||||
ak4113->write(ak4113->private_data, reg, val);
|
||||
if (reg < sizeof(ak4113->regmap))
|
||||
ak4113->regmap[reg] = val;
|
||||
}
|
||||
|
||||
static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
|
||||
{
|
||||
return ak4113->read(ak4113->private_data, reg);
|
||||
}
|
||||
|
||||
static void snd_ak4113_free(struct ak4113 *chip)
|
||||
{
|
||||
chip->init = 1; /* don't schedule new work */
|
||||
mb();
|
||||
cancel_delayed_work(&chip->work);
|
||||
flush_scheduled_work();
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static int snd_ak4113_dev_free(struct snd_device *device)
|
||||
{
|
||||
struct ak4113 *chip = device->device_data;
|
||||
snd_ak4113_free(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
|
||||
ak4113_write_t *write, const unsigned char pgm[5],
|
||||
void *private_data, struct ak4113 **r_ak4113)
|
||||
{
|
||||
struct ak4113 *chip;
|
||||
int err = 0;
|
||||
unsigned char reg;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_ak4113_dev_free,
|
||||
};
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock_init(&chip->lock);
|
||||
chip->card = card;
|
||||
chip->read = read;
|
||||
chip->write = write;
|
||||
chip->private_data = private_data;
|
||||
INIT_DELAYED_WORK(&chip->work, ak4113_stats);
|
||||
|
||||
for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
|
||||
chip->regmap[reg] = pgm[reg];
|
||||
ak4113_init_regs(chip);
|
||||
|
||||
chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
|
||||
AK4113_CINT | AK4113_STC);
|
||||
chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
|
||||
chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
if (err < 0)
|
||||
goto __fail;
|
||||
|
||||
if (r_ak4113)
|
||||
*r_ak4113 = chip;
|
||||
return 0;
|
||||
|
||||
__fail:
|
||||
snd_ak4113_free(chip);
|
||||
return err < 0 ? err : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_create);
|
||||
|
||||
void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
|
||||
unsigned char mask, unsigned char val)
|
||||
{
|
||||
if (reg >= AK4113_WRITABLE_REGS)
|
||||
return;
|
||||
reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
|
||||
|
||||
static void ak4113_init_regs(struct ak4113 *chip)
|
||||
{
|
||||
unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
|
||||
|
||||
/* bring the chip to reset state and powerdown state */
|
||||
reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
|
||||
udelay(200);
|
||||
/* release reset, but leave powerdown */
|
||||
reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
|
||||
udelay(200);
|
||||
for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
|
||||
reg_write(chip, reg, chip->regmap[reg]);
|
||||
/* release powerdown, everything is initialized now */
|
||||
reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
|
||||
}
|
||||
|
||||
void snd_ak4113_reinit(struct ak4113 *chip)
|
||||
{
|
||||
chip->init = 1;
|
||||
mb();
|
||||
flush_scheduled_work();
|
||||
ak4113_init_regs(chip);
|
||||
/* bring up statistics / event queing */
|
||||
chip->init = 0;
|
||||
if (chip->kctls[0])
|
||||
schedule_delayed_work(&chip->work, HZ / 10);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
|
||||
|
||||
static unsigned int external_rate(unsigned char rcs1)
|
||||
{
|
||||
switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
|
||||
case AK4113_FS_8000HZ:
|
||||
return 8000;
|
||||
case AK4113_FS_11025HZ:
|
||||
return 11025;
|
||||
case AK4113_FS_16000HZ:
|
||||
return 16000;
|
||||
case AK4113_FS_22050HZ:
|
||||
return 22050;
|
||||
case AK4113_FS_24000HZ:
|
||||
return 24000;
|
||||
case AK4113_FS_32000HZ:
|
||||
return 32000;
|
||||
case AK4113_FS_44100HZ:
|
||||
return 44100;
|
||||
case AK4113_FS_48000HZ:
|
||||
return 48000;
|
||||
case AK4113_FS_64000HZ:
|
||||
return 64000;
|
||||
case AK4113_FS_88200HZ:
|
||||
return 88200;
|
||||
case AK4113_FS_96000HZ:
|
||||
return 96000;
|
||||
case AK4113_FS_176400HZ:
|
||||
return 176400;
|
||||
case AK4113_FS_192000HZ:
|
||||
return 192000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = LONG_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
long *ptr;
|
||||
|
||||
spin_lock_irq(&chip->lock);
|
||||
ptr = (long *)(((char *)chip) + kcontrol->private_value);
|
||||
ucontrol->value.integer.value[0] = *ptr;
|
||||
*ptr = 0;
|
||||
spin_unlock_irq(&chip->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define snd_ak4113_in_bit_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned char reg = kcontrol->private_value & 0xff;
|
||||
unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
|
||||
unsigned char inv = (kcontrol->private_value >> 31) & 1;
|
||||
|
||||
ucontrol->value.integer.value[0] =
|
||||
((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] =
|
||||
(AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int change;
|
||||
u8 old_val;
|
||||
|
||||
spin_lock_irq(&chip->lock);
|
||||
old_val = chip->regmap[AK4113_REG_IO1];
|
||||
change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
|
||||
if (change)
|
||||
reg_write(chip, AK4113_REG_IO1,
|
||||
(old_val & (~AK4113_IPS(0xff))) |
|
||||
(AK4113_IPS(ucontrol->value.integer.value[0])));
|
||||
spin_unlock_irq(&chip->lock);
|
||||
return change;
|
||||
}
|
||||
|
||||
static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 192000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
|
||||
AK4113_REG_RCS1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
|
||||
ucontrol->value.iec958.status[i] = reg_read(chip,
|
||||
AK4113_REG_RXCSB0 + i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 0xffff;
|
||||
uinfo->count = 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned short tmp;
|
||||
|
||||
ucontrol->value.integer.value[0] = 0xf8f2;
|
||||
ucontrol->value.integer.value[1] = 0x4e1f;
|
||||
tmp = reg_read(chip, AK4113_REG_Pc0) |
|
||||
(reg_read(chip, AK4113_REG_Pc1) << 8);
|
||||
ucontrol->value.integer.value[2] = tmp;
|
||||
tmp = reg_read(chip, AK4113_REG_Pd0) |
|
||||
(reg_read(chip, AK4113_REG_Pd1) << 8);
|
||||
ucontrol->value.integer.value[3] = tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = AK4113_REG_QSUB_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
|
||||
ucontrol->value.bytes.data[i] = reg_read(chip,
|
||||
AK4113_REG_QSUB_ADDR + i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't forget to change AK4113_CONTROLS define!!! */
|
||||
static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Parity Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_error_info,
|
||||
.get = snd_ak4113_in_error_get,
|
||||
.private_value = offsetof(struct ak4113, parity_errors),
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 V-Bit Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_error_info,
|
||||
.get = snd_ak4113_in_error_get,
|
||||
.private_value = offsetof(struct ak4113, v_bit_errors),
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 C-CRC Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_error_info,
|
||||
.get = snd_ak4113_in_error_get,
|
||||
.private_value = offsetof(struct ak4113, ccrc_errors),
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Q-CRC Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_error_info,
|
||||
.get = snd_ak4113_in_error_get,
|
||||
.private_value = offsetof(struct ak4113, qcrc_errors),
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 External Rate",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_rate_info,
|
||||
.get = snd_ak4113_rate_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.info = snd_ak4113_spdif_mask_info,
|
||||
.get = snd_ak4113_spdif_mask_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_spdif_info,
|
||||
.get = snd_ak4113_spdif_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Preample Capture Default",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_spdif_pinfo,
|
||||
.get = snd_ak4113_spdif_pget,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Q-subcode Capture Default",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_spdif_qinfo,
|
||||
.get = snd_ak4113_spdif_qget,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Audio",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_bit_info,
|
||||
.get = snd_ak4113_in_bit_get,
|
||||
.private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Non-PCM Bitstream",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_bit_info,
|
||||
.get = snd_ak4113_in_bit_get,
|
||||
.private_value = (0<<8) | AK4113_REG_RCS1,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 DTS Bitstream",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ak4113_in_bit_info,
|
||||
.get = snd_ak4113_in_bit_get,
|
||||
.private_value = (1<<8) | AK4113_REG_RCS1,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "AK4113 Input Select",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE,
|
||||
.info = snd_ak4113_rx_info,
|
||||
.get = snd_ak4113_rx_get,
|
||||
.put = snd_ak4113_rx_put,
|
||||
}
|
||||
};
|
||||
|
||||
static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct ak4113 *ak4113 = entry->private_data;
|
||||
int reg, val;
|
||||
/* all ak4113 registers 0x00 - 0x1c */
|
||||
for (reg = 0; reg < 0x1d; reg++) {
|
||||
val = reg_read(ak4113, reg);
|
||||
snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_ak4113_proc_init(struct ak4113 *ak4113)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
|
||||
snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
|
||||
}
|
||||
|
||||
int snd_ak4113_build(struct ak4113 *ak4113,
|
||||
struct snd_pcm_substream *cap_substream)
|
||||
{
|
||||
struct snd_kcontrol *kctl;
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
if (snd_BUG_ON(!cap_substream))
|
||||
return -EINVAL;
|
||||
ak4113->substream = cap_substream;
|
||||
for (idx = 0; idx < AK4113_CONTROLS; idx++) {
|
||||
kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
|
||||
if (kctl == NULL)
|
||||
return -ENOMEM;
|
||||
kctl->id.device = cap_substream->pcm->device;
|
||||
kctl->id.subdevice = cap_substream->number;
|
||||
err = snd_ctl_add(ak4113->card, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ak4113->kctls[idx] = kctl;
|
||||
}
|
||||
snd_ak4113_proc_init(ak4113);
|
||||
/* trigger workq */
|
||||
schedule_delayed_work(&ak4113->work, HZ / 10);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_build);
|
||||
|
||||
int snd_ak4113_external_rate(struct ak4113 *ak4113)
|
||||
{
|
||||
unsigned char rcs1;
|
||||
|
||||
rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
|
||||
return external_rate(rcs1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
|
||||
|
||||
int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime =
|
||||
ak4113->substream ? ak4113->substream->runtime : NULL;
|
||||
unsigned long _flags;
|
||||
int res = 0;
|
||||
unsigned char rcs0, rcs1, rcs2;
|
||||
unsigned char c0, c1;
|
||||
|
||||
rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
|
||||
if (flags & AK4113_CHECK_NO_STAT)
|
||||
goto __rate;
|
||||
rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
|
||||
rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
|
||||
spin_lock_irqsave(&ak4113->lock, _flags);
|
||||
if (rcs0 & AK4113_PAR)
|
||||
ak4113->parity_errors++;
|
||||
if (rcs0 & AK4113_V)
|
||||
ak4113->v_bit_errors++;
|
||||
if (rcs2 & AK4113_CCRC)
|
||||
ak4113->ccrc_errors++;
|
||||
if (rcs2 & AK4113_QCRC)
|
||||
ak4113->qcrc_errors++;
|
||||
c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
|
||||
AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
|
||||
(rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
|
||||
AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
|
||||
c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
|
||||
AK4113_DAT | 0xf0)) ^
|
||||
(rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
|
||||
AK4113_DAT | 0xf0));
|
||||
ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
|
||||
ak4113->rcs1 = rcs1;
|
||||
ak4113->rcs2 = rcs2;
|
||||
spin_unlock_irqrestore(&ak4113->lock, _flags);
|
||||
|
||||
if (rcs0 & AK4113_PAR)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[0]->id);
|
||||
if (rcs0 & AK4113_V)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[1]->id);
|
||||
if (rcs2 & AK4113_CCRC)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[2]->id);
|
||||
if (rcs2 & AK4113_QCRC)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[3]->id);
|
||||
|
||||
/* rate change */
|
||||
if (c1 & 0xf0)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[4]->id);
|
||||
|
||||
if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[6]->id);
|
||||
if (c0 & AK4113_QINT)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[8]->id);
|
||||
|
||||
if (c0 & AK4113_AUDION)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[9]->id);
|
||||
if (c1 & AK4113_NPCM)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[10]->id);
|
||||
if (c1 & AK4113_DTSCD)
|
||||
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&ak4113->kctls[11]->id);
|
||||
|
||||
if (ak4113->change_callback && (c0 | c1) != 0)
|
||||
ak4113->change_callback(ak4113, c0, c1);
|
||||
|
||||
__rate:
|
||||
/* compare rate */
|
||||
res = external_rate(rcs1);
|
||||
if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
|
||||
(runtime->rate != res)) {
|
||||
snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
|
||||
if (snd_pcm_running(ak4113->substream)) {
|
||||
/*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
|
||||
* runtime->rate, res); */
|
||||
snd_pcm_stop(ak4113->substream,
|
||||
SNDRV_PCM_STATE_DRAINING);
|
||||
wake_up(&runtime->sleep);
|
||||
res = 1;
|
||||
}
|
||||
snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
|
||||
|
||||
static void ak4113_stats(struct work_struct *work)
|
||||
{
|
||||
struct ak4113 *chip = container_of(work, struct ak4113, work.work);
|
||||
|
||||
if (!chip->init)
|
||||
snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
|
||||
|
||||
schedule_delayed_work(&chip->work, HZ / 10);
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
#include <sound/control.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/ak4xxx-adda.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
|
||||
|
@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
|
|||
static void ak4524_reset(struct snd_akm4xxx *ak, int state)
|
||||
{
|
||||
unsigned int chip;
|
||||
unsigned char reg, maxreg;
|
||||
unsigned char reg;
|
||||
|
||||
if (ak->type == SND_AK4528)
|
||||
maxreg = 0x06;
|
||||
else
|
||||
maxreg = 0x08;
|
||||
for (chip = 0; chip < ak->num_dacs/2; chip++) {
|
||||
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
|
||||
if (state)
|
||||
continue;
|
||||
/* DAC volumes */
|
||||
for (reg = 0x04; reg < maxreg; reg++)
|
||||
for (reg = 0x04; reg < ak->total_regs; reg++)
|
||||
snd_akm4xxx_write(ak, chip, reg,
|
||||
snd_akm4xxx_get(ak, chip, reg));
|
||||
}
|
||||
}
|
||||
|
||||
/* reset procedure for AK4355 and AK4358 */
|
||||
static void ak435X_reset(struct snd_akm4xxx *ak, int state,
|
||||
unsigned char total_regs)
|
||||
static void ak435X_reset(struct snd_akm4xxx *ak, int state)
|
||||
{
|
||||
unsigned char reg;
|
||||
|
||||
|
@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state,
|
|||
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
|
||||
return;
|
||||
}
|
||||
for (reg = 0x00; reg < total_regs; reg++)
|
||||
for (reg = 0x00; reg < ak->total_regs; reg++)
|
||||
if (reg != 0x01)
|
||||
snd_akm4xxx_write(ak, 0, reg,
|
||||
snd_akm4xxx_get(ak, 0, reg));
|
||||
|
@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
|
|||
{
|
||||
unsigned int chip;
|
||||
unsigned char reg;
|
||||
|
||||
for (chip = 0; chip < ak->num_dacs/2; chip++) {
|
||||
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
|
||||
if (state)
|
||||
continue;
|
||||
for (reg = 0x01; reg < 0x05; reg++)
|
||||
for (reg = 0x01; reg < ak->total_regs; reg++)
|
||||
snd_akm4xxx_write(ak, chip, reg,
|
||||
snd_akm4xxx_get(ak, chip, reg));
|
||||
}
|
||||
|
@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
|
|||
switch (ak->type) {
|
||||
case SND_AK4524:
|
||||
case SND_AK4528:
|
||||
case SND_AK4620:
|
||||
ak4524_reset(ak, state);
|
||||
break;
|
||||
case SND_AK4529:
|
||||
/* FIXME: needed for ak4529? */
|
||||
break;
|
||||
case SND_AK4355:
|
||||
ak435X_reset(ak, state, 0x0b);
|
||||
ak435X_reset(ak, state);
|
||||
break;
|
||||
case SND_AK4358:
|
||||
ak435X_reset(ak, state, 0x10);
|
||||
ak435X_reset(ak, state);
|
||||
break;
|
||||
case SND_AK4381:
|
||||
ak4381_reset(ak, state);
|
||||
|
@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
|
|||
* Volume conversion table for non-linear volumes
|
||||
* from -63.5dB (mute) to 0dB step 0.5dB
|
||||
*
|
||||
* Used for AK4524 input/ouput attenuation, AK4528, and
|
||||
* Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
|
||||
* AK5365 input attenuation
|
||||
*/
|
||||
static const unsigned char vol_cvt_datt[128] = {
|
||||
|
@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
|
|||
0x00, 0x0f, /* 0: power-up, un-reset */
|
||||
0xff, 0xff
|
||||
};
|
||||
static const unsigned char inits_ak4620[] = {
|
||||
0x00, 0x07, /* 0: normal */
|
||||
0x01, 0x00, /* 0: reset */
|
||||
0x01, 0x02, /* 1: RSTAD */
|
||||
0x01, 0x03, /* 1: RSTDA */
|
||||
0x01, 0x0f, /* 1: normal */
|
||||
0x02, 0x60, /* 2: 24bit I2S */
|
||||
0x03, 0x01, /* 3: deemphasis off */
|
||||
0x04, 0x00, /* 4: LIN muted */
|
||||
0x05, 0x00, /* 5: RIN muted */
|
||||
0x06, 0x00, /* 6: LOUT muted */
|
||||
0x07, 0x00, /* 7: ROUT muted */
|
||||
0xff, 0xff
|
||||
};
|
||||
|
||||
int chip, num_chips;
|
||||
int chip;
|
||||
const unsigned char *ptr, *inits;
|
||||
unsigned char reg, data;
|
||||
|
||||
|
@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
|
|||
switch (ak->type) {
|
||||
case SND_AK4524:
|
||||
inits = inits_ak4524;
|
||||
num_chips = ak->num_dacs / 2;
|
||||
ak->num_chips = ak->num_dacs / 2;
|
||||
ak->name = "ak4524";
|
||||
ak->total_regs = 0x08;
|
||||
break;
|
||||
case SND_AK4528:
|
||||
inits = inits_ak4528;
|
||||
num_chips = ak->num_dacs / 2;
|
||||
ak->num_chips = ak->num_dacs / 2;
|
||||
ak->name = "ak4528";
|
||||
ak->total_regs = 0x06;
|
||||
break;
|
||||
case SND_AK4529:
|
||||
inits = inits_ak4529;
|
||||
num_chips = 1;
|
||||
ak->num_chips = 1;
|
||||
ak->name = "ak4529";
|
||||
ak->total_regs = 0x0d;
|
||||
break;
|
||||
case SND_AK4355:
|
||||
inits = inits_ak4355;
|
||||
num_chips = 1;
|
||||
ak->num_chips = 1;
|
||||
ak->name = "ak4355";
|
||||
ak->total_regs = 0x0b;
|
||||
break;
|
||||
case SND_AK4358:
|
||||
inits = inits_ak4358;
|
||||
num_chips = 1;
|
||||
ak->num_chips = 1;
|
||||
ak->name = "ak4358";
|
||||
ak->total_regs = 0x10;
|
||||
break;
|
||||
case SND_AK4381:
|
||||
inits = inits_ak4381;
|
||||
num_chips = ak->num_dacs / 2;
|
||||
ak->num_chips = ak->num_dacs / 2;
|
||||
ak->name = "ak4381";
|
||||
ak->total_regs = 0x05;
|
||||
break;
|
||||
case SND_AK5365:
|
||||
/* FIXME: any init sequence? */
|
||||
ak->num_chips = 1;
|
||||
ak->name = "ak5365";
|
||||
ak->total_regs = 0x08;
|
||||
return;
|
||||
case SND_AK4620:
|
||||
inits = inits_ak4620;
|
||||
ak->num_chips = ak->num_dacs / 2;
|
||||
ak->name = "ak4620";
|
||||
ak->total_regs = 0x08;
|
||||
break;
|
||||
default:
|
||||
snd_BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
for (chip = 0; chip < num_chips; chip++) {
|
||||
for (chip = 0; chip < ak->num_chips; chip++) {
|
||||
ptr = inits;
|
||||
while (*ptr != 0xff) {
|
||||
reg = *ptr++;
|
||||
data = *ptr++;
|
||||
snd_akm4xxx_write(ak, chip, reg, data);
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
|
|||
AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
|
||||
knew.tlv.p = db_scale_linear;
|
||||
break;
|
||||
case SND_AK4620:
|
||||
/* register 6 & 7 */
|
||||
knew.private_value =
|
||||
AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
|
||||
knew.tlv.p = db_scale_linear;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
|
|||
|
||||
static int build_adc_controls(struct snd_akm4xxx *ak)
|
||||
{
|
||||
int idx, err, mixer_ch, num_stereo;
|
||||
int idx, err, mixer_ch, num_stereo, max_steps;
|
||||
struct snd_kcontrol_new knew;
|
||||
|
||||
mixer_ch = 0;
|
||||
if (ak->type == SND_AK4528)
|
||||
return 0; /* no controls */
|
||||
for (idx = 0; idx < ak->num_adcs;) {
|
||||
memset(&knew, 0, sizeof(knew));
|
||||
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
|
||||
|
@ -733,12 +773,11 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
|
|||
}
|
||||
/* register 4 & 5 */
|
||||
if (ak->type == SND_AK5365)
|
||||
knew.private_value =
|
||||
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
|
||||
AK_VOL_CVT | AK_IPGA;
|
||||
max_steps = 152;
|
||||
else
|
||||
max_steps = 164;
|
||||
knew.private_value =
|
||||
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
|
||||
AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
|
||||
AK_VOL_CVT | AK_IPGA;
|
||||
knew.tlv.p = db_scale_vol_datt;
|
||||
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
|
||||
|
@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
|
|||
switch (ak->type) {
|
||||
case SND_AK4524:
|
||||
case SND_AK4528:
|
||||
case SND_AK4620:
|
||||
/* register 3 */
|
||||
knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
|
||||
break;
|
||||
|
@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void proc_regs_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
|
||||
int reg, val, chip;
|
||||
for (chip = 0; chip < ak->num_chips; chip++) {
|
||||
for (reg = 0; reg < ak->total_regs; reg++) {
|
||||
val = snd_akm4xxx_get(ak, chip, reg);
|
||||
snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
|
||||
reg, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int proc_init(struct snd_akm4xxx *ak)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
int err;
|
||||
err = snd_card_proc_new(ak->card, ak->name, &entry);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_info_set_text_ops(entry, ak, proc_regs_read);
|
||||
return 0;
|
||||
}
|
||||
#else /* !CONFIG_PROC_FS */
|
||||
static int proc_init(struct snd_akm4xxx *ak) {}
|
||||
#endif
|
||||
|
||||
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
|
||||
{
|
||||
int err, num_emphs;
|
||||
|
@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
|
|||
err = build_adc_controls(ak);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
|
||||
num_emphs = 1;
|
||||
else if (ak->type == SND_AK4620)
|
||||
num_emphs = 0;
|
||||
else
|
||||
num_emphs = ak->num_dacs / 2;
|
||||
err = build_deemphasis(ak, num_emphs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = proc_init(ak);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
|
||||
|
||||
static int __init alsa_akm4xxx_module_init(void)
|
||||
|
|
|
@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
|
|||
Say Y here to build a digital beep interface for HD-audio
|
||||
driver. This interface is used to generate digital beeps.
|
||||
|
||||
config SND_HDA_INPUT_BEEP_MODE
|
||||
int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
|
||||
depends on SND_HDA_INPUT_BEEP=y
|
||||
default "1"
|
||||
range 0 2
|
||||
help
|
||||
Set 0 to disable the digital beep interface for HD-audio by default.
|
||||
Set 1 to always enable the digital beep interface for HD-audio by
|
||||
default. Set 2 to control the beep device registration to input
|
||||
layer using a "Beep Switch" in mixer applications.
|
||||
|
||||
config SND_HDA_INPUT_JACK
|
||||
bool "Support jack plugging notification via input layer"
|
||||
depends on INPUT=y || INPUT=SND_HDA_INTEL
|
||||
depends on INPUT=y || INPUT=SND
|
||||
select SND_JACK
|
||||
help
|
||||
Say Y here to enable the jack plugging notification via
|
||||
|
|
|
@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
||||
static void snd_hda_do_detach(struct hda_beep *beep)
|
||||
{
|
||||
input_unregister_device(beep->dev);
|
||||
beep->dev = NULL;
|
||||
cancel_work_sync(&beep->beep_work);
|
||||
/* turn off beep for sure */
|
||||
snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
|
||||
AC_VERB_SET_BEEP_CONTROL, 0);
|
||||
}
|
||||
|
||||
static int snd_hda_do_attach(struct hda_beep *beep)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
struct hda_beep *beep;
|
||||
struct hda_codec *codec = beep->codec;
|
||||
int err;
|
||||
|
||||
if (!snd_hda_get_bool_hint(codec, "beep"))
|
||||
return 0; /* disabled explicitly */
|
||||
|
||||
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
|
||||
if (beep == NULL)
|
||||
return -ENOMEM;
|
||||
snprintf(beep->phys, sizeof(beep->phys),
|
||||
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
kfree(beep);
|
||||
printk(KERN_INFO "hda_beep: unable to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
|||
err = input_register_device(input_dev);
|
||||
if (err < 0) {
|
||||
input_free_device(input_dev);
|
||||
kfree(beep);
|
||||
printk(KERN_INFO "hda_beep: unable to register input device\n");
|
||||
return err;
|
||||
}
|
||||
beep->dev = input_dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_hda_do_register(struct work_struct *work)
|
||||
{
|
||||
struct hda_beep *beep =
|
||||
container_of(work, struct hda_beep, register_work);
|
||||
|
||||
mutex_lock(&beep->mutex);
|
||||
if (beep->enabled && !beep->dev)
|
||||
snd_hda_do_attach(beep);
|
||||
mutex_unlock(&beep->mutex);
|
||||
}
|
||||
|
||||
static void snd_hda_do_unregister(struct work_struct *work)
|
||||
{
|
||||
struct hda_beep *beep =
|
||||
container_of(work, struct hda_beep, unregister_work.work);
|
||||
|
||||
mutex_lock(&beep->mutex);
|
||||
if (!beep->enabled && beep->dev)
|
||||
snd_hda_do_detach(beep);
|
||||
mutex_unlock(&beep->mutex);
|
||||
}
|
||||
|
||||
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
|
||||
{
|
||||
struct hda_beep *beep = codec->beep;
|
||||
enable = !!enable;
|
||||
if (beep == NULL)
|
||||
return 0;
|
||||
if (beep->enabled != enable) {
|
||||
beep->enabled = enable;
|
||||
if (!enable) {
|
||||
/* turn off beep */
|
||||
snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
|
||||
AC_VERB_SET_BEEP_CONTROL, 0);
|
||||
}
|
||||
if (beep->mode == HDA_BEEP_MODE_SWREG) {
|
||||
if (enable) {
|
||||
cancel_delayed_work(&beep->unregister_work);
|
||||
schedule_work(&beep->register_work);
|
||||
} else {
|
||||
schedule_delayed_work(&beep->unregister_work,
|
||||
HZ);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
|
||||
|
||||
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
||||
{
|
||||
struct hda_beep *beep;
|
||||
|
||||
if (!snd_hda_get_bool_hint(codec, "beep"))
|
||||
return 0; /* disabled explicitly by hints */
|
||||
if (codec->beep_mode == HDA_BEEP_MODE_OFF)
|
||||
return 0; /* disabled by module option */
|
||||
|
||||
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
|
||||
if (beep == NULL)
|
||||
return -ENOMEM;
|
||||
snprintf(beep->phys, sizeof(beep->phys),
|
||||
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
|
||||
/* enable linear scale */
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
|
||||
|
||||
beep->nid = nid;
|
||||
beep->dev = input_dev;
|
||||
beep->codec = codec;
|
||||
beep->enabled = 1;
|
||||
beep->mode = codec->beep_mode;
|
||||
codec->beep = beep;
|
||||
|
||||
INIT_WORK(&beep->register_work, &snd_hda_do_register);
|
||||
INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
|
||||
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
|
||||
mutex_init(&beep->mutex);
|
||||
|
||||
if (beep->mode == HDA_BEEP_MODE_ON) {
|
||||
beep->enabled = 1;
|
||||
snd_hda_do_register(&beep->register_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
|
||||
|
@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
|
|||
{
|
||||
struct hda_beep *beep = codec->beep;
|
||||
if (beep) {
|
||||
cancel_work_sync(&beep->beep_work);
|
||||
|
||||
input_unregister_device(beep->dev);
|
||||
kfree(beep);
|
||||
cancel_work_sync(&beep->register_work);
|
||||
cancel_delayed_work(&beep->unregister_work);
|
||||
if (beep->enabled)
|
||||
snd_hda_do_detach(beep);
|
||||
codec->beep = NULL;
|
||||
kfree(beep);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
|
||||
|
|
|
@ -24,19 +24,29 @@
|
|||
|
||||
#include "hda_codec.h"
|
||||
|
||||
#define HDA_BEEP_MODE_OFF 0
|
||||
#define HDA_BEEP_MODE_ON 1
|
||||
#define HDA_BEEP_MODE_SWREG 2
|
||||
|
||||
/* beep information */
|
||||
struct hda_beep {
|
||||
struct input_dev *dev;
|
||||
struct hda_codec *codec;
|
||||
unsigned int mode;
|
||||
char phys[32];
|
||||
int tone;
|
||||
hda_nid_t nid;
|
||||
unsigned int enabled:1;
|
||||
unsigned int request_enable:1;
|
||||
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
|
||||
struct work_struct register_work; /* registration work */
|
||||
struct delayed_work unregister_work; /* unregistration work */
|
||||
struct work_struct beep_work; /* scheduled task for beep event */
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
|
||||
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
|
||||
void snd_hda_detach_beep_device(struct hda_codec *codec);
|
||||
#else
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -286,6 +286,10 @@ enum {
|
|||
#define AC_PWRST_D1SUP (1<<1)
|
||||
#define AC_PWRST_D2SUP (1<<2)
|
||||
#define AC_PWRST_D3SUP (1<<3)
|
||||
#define AC_PWRST_D3COLDSUP (1<<4)
|
||||
#define AC_PWRST_S3D3COLDSUP (1<<29)
|
||||
#define AC_PWRST_CLKSTOP (1<<30)
|
||||
#define AC_PWRST_EPSS (1U<<31)
|
||||
|
||||
/* Power state values */
|
||||
#define AC_PWRST_SETTING (0xf<<0)
|
||||
|
@ -674,6 +678,7 @@ struct hda_codec_ops {
|
|||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
|
||||
#endif
|
||||
void (*reboot_notify)(struct hda_codec *codec);
|
||||
};
|
||||
|
||||
/* record for amp information cache */
|
||||
|
@ -771,6 +776,7 @@ struct hda_codec {
|
|||
|
||||
/* beep device */
|
||||
struct hda_beep *beep;
|
||||
unsigned int beep_mode;
|
||||
|
||||
/* widget capabilities cache */
|
||||
unsigned int num_nodes;
|
||||
|
@ -811,6 +817,9 @@ struct hda_codec {
|
|||
unsigned int power_transition :1; /* power-state in transition */
|
||||
int power_count; /* current (global) power refcount */
|
||||
struct delayed_work power_work; /* delayed task for powerdown */
|
||||
unsigned long power_on_acct;
|
||||
unsigned long power_off_acct;
|
||||
unsigned long power_jiffies;
|
||||
#endif
|
||||
|
||||
/* codec-specific additional proc output */
|
||||
|
@ -910,6 +919,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
|
|||
* Misc
|
||||
*/
|
||||
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
|
||||
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
|
||||
|
||||
/*
|
||||
* power management
|
||||
|
@ -933,6 +943,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
|
|||
void snd_hda_power_up(struct hda_codec *codec);
|
||||
void snd_hda_power_down(struct hda_codec *codec);
|
||||
#define snd_hda_codec_needs_resume(codec) codec->power_count
|
||||
void snd_hda_update_power_acct(struct hda_codec *codec);
|
||||
#else
|
||||
static inline void snd_hda_power_up(struct hda_codec *codec) {}
|
||||
static inline void snd_hda_power_down(struct hda_codec *codec) {}
|
||||
|
|
|
@ -309,17 +309,12 @@ out_fail:
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
}
|
||||
|
||||
static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int eldv;
|
||||
int present;
|
||||
|
||||
present = hdmi_present_sense(codec, nid);
|
||||
present = snd_hda_pin_sense(codec, nid);
|
||||
eldv = (present & AC_PINSENSE_ELDV);
|
||||
present = (present & AC_PINSENSE_PRESENCE);
|
||||
|
||||
|
@ -477,6 +472,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
|
|||
[4 ... 7] = "reserved"
|
||||
};
|
||||
|
||||
snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
|
||||
snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
|
||||
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
|
||||
snd_iprintf(buffer, "connection_type\t\t%s\n",
|
||||
eld_connection_type_names[e->conn_type]);
|
||||
|
@ -518,7 +515,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
|
|||
* monitor_name manufacture_id product_id
|
||||
* eld_version edid_version
|
||||
*/
|
||||
if (!strcmp(name, "connection_type"))
|
||||
if (!strcmp(name, "monitor_present"))
|
||||
e->monitor_present = val;
|
||||
else if (!strcmp(name, "eld_valid"))
|
||||
e->eld_valid = val;
|
||||
else if (!strcmp(name, "connection_type"))
|
||||
e->conn_type = val;
|
||||
else if (!strcmp(name, "port_id"))
|
||||
e->port_id = val;
|
||||
|
@ -560,13 +561,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
|
|||
}
|
||||
|
||||
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
|
||||
int index)
|
||||
{
|
||||
char name[32];
|
||||
struct snd_info_entry *entry;
|
||||
int err;
|
||||
|
||||
snprintf(name, sizeof(name), "eld#%d", codec->addr);
|
||||
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
|
||||
err = snd_card_proc_new(codec->bus->card, name, &entry);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
|
@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
if (is_loopback)
|
||||
add_input_loopback(codec, node->nid, HDA_INPUT, index);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
err = snd_hda_ctl_add(codec, node->nid,
|
||||
snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
|
@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
if (is_loopback)
|
||||
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
err = snd_hda_ctl_add(codec, node->nid,
|
||||
snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
|
@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
|
||||
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
err = snd_hda_ctl_add(codec, node->nid,
|
||||
snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
|
@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
|
|||
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
|
||||
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
|
||||
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
err = snd_hda_ctl_add(codec, node->nid,
|
||||
snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
created = 1;
|
||||
|
@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
/* create input MUX if multiple sources are available */
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
|
||||
err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec)
|
|||
HDA_CODEC_VOLUME(name, adc_node->nid,
|
||||
spec->input_mux.items[i].index,
|
||||
HDA_INPUT);
|
||||
err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
err = snd_hda_ctl_add(codec, adc_node->nid,
|
||||
snd_ctl_new1(&knew, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static ssize_t power_on_acct_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
snd_hda_update_power_acct(codec);
|
||||
return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
|
||||
}
|
||||
|
||||
static ssize_t power_off_acct_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
snd_hda_update_power_acct(codec);
|
||||
return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
|
||||
}
|
||||
|
||||
static struct device_attribute power_attrs[] = {
|
||||
__ATTR_RO(power_on_acct),
|
||||
__ATTR_RO(power_off_acct),
|
||||
};
|
||||
|
||||
int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
|
||||
{
|
||||
struct snd_hwdep *hwdep = codec->hwdep;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
|
||||
snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
|
||||
hwdep->device, &power_attrs[i]);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SND_HDA_POWER_SAVE */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
|
||||
/*
|
||||
|
|
|
@ -60,10 +60,14 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
|
|||
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
|
||||
static int probe_only[SNDRV_CARDS];
|
||||
static int single_cmd;
|
||||
static int enable_msi;
|
||||
static int enable_msi = -1;
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
static char *patch[SNDRV_CARDS];
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
|
||||
CONFIG_SND_HDA_INPUT_BEEP_MODE};
|
||||
#endif
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
|
||||
|
@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
|
|||
module_param_array(patch, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
module_param_array(beep_mode, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
|
||||
"(0=off, 1=on, 2=mute switch on/off) (default=1).");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
|
||||
|
@ -404,6 +413,7 @@ struct azx {
|
|||
unsigned short codec_mask;
|
||||
int codec_probe_mask; /* copied from probe_mask option */
|
||||
struct hda_bus *bus;
|
||||
unsigned int beep_mode;
|
||||
|
||||
/* CORB/RIRB */
|
||||
struct azx_rb corb;
|
||||
|
@ -677,6 +687,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
|
|||
}
|
||||
}
|
||||
|
||||
if (!chip->polling_mode) {
|
||||
snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
|
||||
"switching to polling mode: last cmd=0x%08x\n",
|
||||
chip->last_cmd[addr]);
|
||||
chip->polling_mode = 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (chip->msi) {
|
||||
snd_printk(KERN_WARNING SFX "No response from codec, "
|
||||
"disabling MSI: last cmd=0x%08x\n",
|
||||
|
@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
|
|||
goto again;
|
||||
}
|
||||
|
||||
if (!chip->polling_mode) {
|
||||
snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
|
||||
"switching to polling mode: last cmd=0x%08x\n",
|
||||
chip->last_cmd[addr]);
|
||||
chip->polling_mode = 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (chip->probing) {
|
||||
/* If this critical timeout happens during the codec probing
|
||||
* phase, this is likely an access to a non-existing codec
|
||||
|
@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
|
|||
err = snd_hda_codec_new(chip->bus, c, &codec);
|
||||
if (err < 0)
|
||||
continue;
|
||||
codec->beep_mode = chip->beep_mode;
|
||||
codecs++;
|
||||
}
|
||||
}
|
||||
|
@ -2154,6 +2165,7 @@ static int azx_resume(struct pci_dev *pci)
|
|||
static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
|
||||
{
|
||||
struct azx *chip = container_of(nb, struct azx, reboot_notifier);
|
||||
snd_hda_bus_reboot_notify(chip->bus);
|
||||
azx_stop_chip(chip);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
@ -2221,7 +2233,9 @@ static int azx_dev_free(struct snd_device *device)
|
|||
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2304,11 +2318,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* white-list for enable_msi
|
||||
* white/black-list for enable_msi
|
||||
*/
|
||||
static struct snd_pci_quirk msi_white_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
|
||||
SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
|
||||
static struct snd_pci_quirk msi_black_list[] __devinitdata = {
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2316,10 +2328,12 @@ static void __devinit check_msi(struct azx *chip)
|
|||
{
|
||||
const struct snd_pci_quirk *q;
|
||||
|
||||
chip->msi = enable_msi;
|
||||
if (chip->msi)
|
||||
if (enable_msi >= 0) {
|
||||
chip->msi = !!enable_msi;
|
||||
return;
|
||||
q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
|
||||
}
|
||||
chip->msi = 1; /* enable MSI as default */
|
||||
q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
|
||||
if (q) {
|
||||
printk(KERN_INFO
|
||||
"hda_intel: msi for device %04x:%04x set to %d\n",
|
||||
|
@ -2578,6 +2592,10 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
|||
goto out_free;
|
||||
card->private_data = chip;
|
||||
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
chip->beep_mode = beep_mode[dev];
|
||||
#endif
|
||||
|
||||
/* create codec instances */
|
||||
err = azx_codec_create(chip, model[dev]);
|
||||
if (err < 0)
|
||||
|
|
|
@ -23,6 +23,15 @@
|
|||
#ifndef __SOUND_HDA_LOCAL_H
|
||||
#define __SOUND_HDA_LOCAL_H
|
||||
|
||||
/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
|
||||
* the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
|
||||
* snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
|
||||
*
|
||||
* Note that the subdevice field is cleared again before the real registration
|
||||
* in snd_hda_ctl_add(), so that this value won't appear in the outside.
|
||||
*/
|
||||
#define HDA_SUBDEV_NID_FLAG (1U << 31)
|
||||
|
||||
/*
|
||||
* for mixer controls
|
||||
*/
|
||||
|
@ -33,6 +42,7 @@
|
|||
/* mono volume with index (index=0,1,...) (channel=1,2) */
|
||||
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
|
||||
.subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
|
||||
|
@ -53,6 +63,7 @@
|
|||
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
|
||||
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
|
||||
.subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
|
||||
.info = snd_hda_mixer_amp_switch_info, \
|
||||
.get = snd_hda_mixer_amp_switch_get, \
|
||||
.put = snd_hda_mixer_amp_switch_put, \
|
||||
|
@ -66,6 +77,28 @@
|
|||
/* stereo mute switch */
|
||||
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
|
||||
HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
|
||||
#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
|
||||
.subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
|
||||
.info = snd_hda_mixer_amp_switch_info, \
|
||||
.get = snd_hda_mixer_amp_switch_get, \
|
||||
.put = snd_hda_mixer_amp_switch_put_beep, \
|
||||
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
|
||||
#else
|
||||
/* no digital beep - just the standard one */
|
||||
#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
|
||||
HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
|
||||
#endif /* CONFIG_SND_HDA_INPUT_BEEP */
|
||||
/* special beep mono mute switch */
|
||||
#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
|
||||
HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
|
||||
/* special beep stereo mute switch */
|
||||
#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
|
||||
HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
|
||||
|
||||
extern const char *snd_hda_pcm_type_name[];
|
||||
|
||||
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
|
@ -81,6 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
#endif
|
||||
/* lowlevel accessor with caching; use carefully */
|
||||
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int index);
|
||||
|
@ -424,8 +461,16 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
|
|||
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int caps);
|
||||
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
|
||||
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
|
||||
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
|
||||
|
||||
int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
|
||||
struct hda_nid_item {
|
||||
struct snd_kcontrol *kctl;
|
||||
hda_nid_t nid;
|
||||
};
|
||||
|
||||
int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct snd_kcontrol *kctl);
|
||||
void snd_hda_ctls_clear(struct hda_codec *codec);
|
||||
|
||||
/*
|
||||
|
@ -437,6 +482,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
|
|||
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
|
||||
int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
|
||||
#else
|
||||
static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
|
||||
#else
|
||||
|
@ -490,7 +544,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
|||
* AMP control callbacks
|
||||
*/
|
||||
/* retrieve parameters from private_value */
|
||||
#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
|
||||
#define get_amp_nid_(pv) ((pv) & 0xffff)
|
||||
#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
|
||||
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
|
||||
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
|
||||
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
|
||||
|
@ -516,9 +571,11 @@ struct cea_sad {
|
|||
* ELD: EDID Like Data
|
||||
*/
|
||||
struct hdmi_eld {
|
||||
bool monitor_present;
|
||||
bool eld_valid;
|
||||
int eld_size;
|
||||
int baseline_len;
|
||||
int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
|
||||
int eld_ver;
|
||||
int cea_edid_ver;
|
||||
char monitor_name[ELD_MAX_MNL + 1];
|
||||
int manufacture_id;
|
||||
|
@ -541,11 +598,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
|
|||
void snd_hdmi_show_eld(struct hdmi_eld *eld);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
|
||||
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
|
||||
int index);
|
||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
|
||||
#else
|
||||
static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
|
||||
struct hdmi_eld *eld)
|
||||
struct hdmi_eld *eld,
|
||||
int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,21 @@
|
|||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
static char *bits_names(unsigned int bits, char *names[], int size)
|
||||
{
|
||||
int i, n;
|
||||
static char buf[128];
|
||||
|
||||
for (i = 0, n = 0; i < size; i++) {
|
||||
if (bits & (1U<<i) && names[i])
|
||||
n += snprintf(buf + n, sizeof(buf) - n, " %s",
|
||||
names[i]);
|
||||
}
|
||||
buf[n] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *get_wid_type_name(unsigned int wid_value)
|
||||
{
|
||||
static char *names[16] = {
|
||||
|
@ -46,6 +61,41 @@ static const char *get_wid_type_name(unsigned int wid_value)
|
|||
return "UNKNOWN Widget";
|
||||
}
|
||||
|
||||
static void print_nid_mixers(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int i;
|
||||
struct hda_nid_item *items = codec->mixers.list;
|
||||
struct snd_kcontrol *kctl;
|
||||
for (i = 0; i < codec->mixers.used; i++) {
|
||||
if (items[i].nid == nid) {
|
||||
kctl = items[i].kctl;
|
||||
snd_iprintf(buffer,
|
||||
" Control: name=\"%s\", index=%i, device=%i\n",
|
||||
kctl->id.name, kctl->id.index, kctl->id.device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_nid_pcms(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int pcm, type;
|
||||
struct hda_pcm *cpcm;
|
||||
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
|
||||
cpcm = &codec->pcm_info[pcm];
|
||||
for (type = 0; type < 2; type++) {
|
||||
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
|
||||
continue;
|
||||
snd_iprintf(buffer, " Device: name=\"%s\", "
|
||||
"type=\"%s\", device=%i\n",
|
||||
cpcm->name,
|
||||
snd_hda_pcm_type_name[cpcm->pcm_type],
|
||||
cpcm->pcm->device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_amp_caps(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid, int dir)
|
||||
{
|
||||
|
@ -363,8 +413,24 @@ static const char *get_pwr_state(u32 state)
|
|||
static void print_power_state(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
static char *names[] = {
|
||||
[ilog2(AC_PWRST_D0SUP)] = "D0",
|
||||
[ilog2(AC_PWRST_D1SUP)] = "D1",
|
||||
[ilog2(AC_PWRST_D2SUP)] = "D2",
|
||||
[ilog2(AC_PWRST_D3SUP)] = "D3",
|
||||
[ilog2(AC_PWRST_D3COLDSUP)] = "D3cold",
|
||||
[ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold",
|
||||
[ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP",
|
||||
[ilog2(AC_PWRST_EPSS)] = "EPSS",
|
||||
};
|
||||
|
||||
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
|
||||
int pwr = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
if (sup)
|
||||
snd_iprintf(buffer, " Power states: %s\n",
|
||||
bits_names(sup, names, ARRAY_SIZE(names)));
|
||||
|
||||
snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
|
||||
get_pwr_state(pwr & AC_PWRST_SETTING),
|
||||
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
|
||||
|
@ -457,6 +523,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
|
|||
(data & (1<<i)) ? 1 : 0,
|
||||
(unsol & (1<<i)) ? 1 : 0);
|
||||
/* FIXME: add GPO and GPI pin information */
|
||||
print_nid_mixers(buffer, codec, nid);
|
||||
}
|
||||
|
||||
static void print_codec_info(struct snd_info_entry *entry,
|
||||
|
@ -536,6 +603,9 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
snd_iprintf(buffer, " CP");
|
||||
snd_iprintf(buffer, "\n");
|
||||
|
||||
print_nid_mixers(buffer, codec, nid);
|
||||
print_nid_pcms(buffer, codec, nid);
|
||||
|
||||
/* volume knob is a special widget that always have connection
|
||||
* list
|
||||
*/
|
||||
|
|
|
@ -156,15 +156,19 @@ static const char *ad_slave_sws[] = {
|
|||
|
||||
static void ad198x_free_kctls(struct hda_codec *codec);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
/* additional beep mixers; the actual parameters are overwritten at build */
|
||||
static struct snd_kcontrol_new ad_beep_mixer[] = {
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
#define set_beep_amp(spec, nid, idx, dir) \
|
||||
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
|
||||
#else
|
||||
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
|
||||
#endif
|
||||
|
||||
static int ad198x_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
/* create beep controls if needed */
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
if (spec->beep_amp) {
|
||||
struct snd_kcontrol_new *knew;
|
||||
for (knew = ad_beep_mixer; knew->name; knew++) {
|
||||
|
@ -202,11 +207,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
|||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
kctl->private_value = spec->beep_amp;
|
||||
err = snd_hda_ctl_add(codec, kctl);
|
||||
err = snd_hda_ctl_add(codec,
|
||||
get_amp_nid_(spec->beep_amp),
|
||||
kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if we have no master control, let's create it */
|
||||
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
|
||||
|
@ -712,10 +720,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
|
|||
static void ad1986a_automic(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
present = snd_hda_jack_detect(codec, 0x1f);
|
||||
/* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
|
||||
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
|
||||
(present & AC_PINSENSE_PRESENCE) ? 0 : 2);
|
||||
present ? 0 : 2);
|
||||
}
|
||||
|
||||
#define AD1986A_MIC_EVENT 0x36
|
||||
|
@ -754,10 +762,8 @@ static void ad1986a_update_hp(struct hda_codec *codec)
|
|||
static void ad1986a_hp_automute(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
spec->jack_present = !!(present & 0x80000000);
|
||||
spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
|
||||
if (spec->inv_jack_detect)
|
||||
spec->jack_present = !spec->jack_present;
|
||||
ad1986a_update_hp(codec);
|
||||
|
@ -1547,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x06, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x06);
|
||||
snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
}
|
||||
|
@ -1568,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec)
|
|||
};
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x08, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x08);
|
||||
if (present)
|
||||
snd_hda_sequence_write(codec, mic_jack_on);
|
||||
else
|
||||
|
@ -2524,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
|
|||
{
|
||||
if ((res >> 26) != AD1988_HP_EVENT)
|
||||
return;
|
||||
if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
|
||||
if (snd_hda_jack_detect(codec, 0x11))
|
||||
snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
|
||||
else
|
||||
snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
|
||||
|
@ -2569,6 +2573,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
|
|||
knew->name = kstrdup(name, GFP_KERNEL);
|
||||
if (! knew->name)
|
||||
return -ENOMEM;
|
||||
if (get_amp_nid_(val))
|
||||
knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
|
||||
knew->private_value = val;
|
||||
return 0;
|
||||
}
|
||||
|
@ -3768,8 +3774,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x11);
|
||||
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
||||
|
@ -3781,8 +3786,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x14, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x14);
|
||||
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
|
||||
present ? 0 : 1);
|
||||
}
|
||||
|
@ -3817,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
present &= AC_PINSENSE_PRESENCE;
|
||||
if (!present) {
|
||||
present = snd_hda_codec_read(codec, 0x12, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
present &= AC_PINSENSE_PRESENCE;
|
||||
}
|
||||
present = snd_hda_jack_detect(codec, 0x11);
|
||||
if (!present)
|
||||
present = snd_hda_jack_detect(codec, 0x12);
|
||||
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
||||
|
@ -3835,11 +3835,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int idx;
|
||||
|
||||
if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE)
|
||||
if (snd_hda_jack_detect(codec, 0x14))
|
||||
idx = 0;
|
||||
else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE)
|
||||
else if (snd_hda_jack_detect(codec, 0x1c))
|
||||
idx = 4;
|
||||
else
|
||||
idx = 1;
|
||||
|
@ -4008,8 +4006,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
|
|||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE;
|
||||
present = snd_hda_jack_detect(codec, 0x11);
|
||||
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
}
|
||||
|
@ -4117,14 +4114,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
|
|||
/* switch to external mic if plugged */
|
||||
static void ad1984a_touchsmart_automic(struct hda_codec *codec)
|
||||
{
|
||||
if (snd_hda_codec_read(codec, 0x1c, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
|
||||
if (snd_hda_jack_detect(codec, 0x1c))
|
||||
snd_hda_codec_write(codec, 0x0c, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, 0x4);
|
||||
} else {
|
||||
else
|
||||
snd_hda_codec_write(codec, 0x0c, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, 0x5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
|
|||
struct snd_kcontrol_new knew =
|
||||
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
|
||||
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
|
||||
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
|
||||
}
|
||||
|
||||
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
|
||||
|
@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
|
|||
struct snd_kcontrol_new knew =
|
||||
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
|
||||
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
|
||||
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
|
||||
}
|
||||
|
||||
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
|
||||
|
|
|
@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
|
|||
knew.private_value = pval;
|
||||
snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
|
||||
*kctlp = snd_ctl_new1(&knew, codec);
|
||||
return snd_hda_ctl_add(codec, *kctlp);
|
||||
return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
|
||||
}
|
||||
|
||||
static int add_volume(struct hda_codec *codec, const char *name,
|
||||
|
@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name,
|
|||
knew.private_value = pval;
|
||||
snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
|
||||
*kctlp = snd_ctl_new1(&knew, codec);
|
||||
return snd_hda_ctl_add(codec, *kctlp);
|
||||
return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
|
||||
}
|
||||
|
||||
static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
|
||||
|
@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
|
|||
|
||||
spec->vmaster_sw =
|
||||
snd_ctl_make_virtual_master("Master Playback Switch", NULL);
|
||||
err = snd_hda_ctl_add(codec, spec->vmaster_sw);
|
||||
err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
|
||||
spec->vmaster_vol =
|
||||
snd_ctl_make_virtual_master("Master Playback Volume", tlv);
|
||||
err = snd_hda_ctl_add(codec, spec->vmaster_vol);
|
||||
err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec)
|
|||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
kctl->private_value = (long)spec->capture_bind[i];
|
||||
err = snd_hda_ctl_add(codec, kctl);
|
||||
err = snd_hda_ctl_add(codec, 0, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spec->num_inputs > 1 && !spec->mic_detect) {
|
||||
err = snd_hda_ctl_add(codec,
|
||||
err = snd_hda_ctl_add(codec, 0,
|
||||
snd_ctl_new1(&cs_capture_source, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec)
|
|||
{
|
||||
struct cs_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
unsigned int caps, present, hp_present;
|
||||
unsigned int caps, hp_present;
|
||||
hda_nid_t nid;
|
||||
int i;
|
||||
|
||||
|
@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec)
|
|||
caps = snd_hda_query_pin_caps(codec, nid);
|
||||
if (!(caps & AC_PINCAP_PRES_DETECT))
|
||||
continue;
|
||||
if (caps & AC_PINCAP_TRIG_REQ)
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_SENSE, 0);
|
||||
present = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
|
||||
hp_present = snd_hda_jack_detect(codec, nid);
|
||||
if (hp_present)
|
||||
break;
|
||||
}
|
||||
|
@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec)
|
|||
struct cs_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
hda_nid_t nid;
|
||||
unsigned int caps, present;
|
||||
unsigned int present;
|
||||
|
||||
nid = cfg->input_pins[spec->automic_idx];
|
||||
caps = snd_hda_query_pin_caps(codec, nid);
|
||||
if (caps & AC_PINCAP_TRIG_REQ)
|
||||
snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
|
||||
present = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
if (present & AC_PINSENSE_PRESENCE)
|
||||
present = snd_hda_jack_detect(codec, nid);
|
||||
if (present)
|
||||
change_cur_input(codec, spec->automic_idx, 0);
|
||||
else {
|
||||
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
|
||||
|
|
|
@ -397,9 +397,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
|
|||
for (i = 0; i < spec->jacks.used; i++) {
|
||||
if (jacks->nid == nid) {
|
||||
unsigned int present;
|
||||
present = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE;
|
||||
present = snd_hda_jack_detect(codec, nid);
|
||||
|
||||
present = (present) ? jacks->type : 0 ;
|
||||
|
||||
|
@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec)
|
|||
};
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x12, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x12);
|
||||
if (present)
|
||||
snd_hda_sequence_write(codec, mic_jack_on);
|
||||
else
|
||||
|
@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
|
|||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int bits;
|
||||
|
||||
spec->hp_present = snd_hda_codec_read(codec, 0x11, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
spec->hp_present = snd_hda_jack_detect(codec, 0x11);
|
||||
|
||||
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
|
||||
snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
|
||||
|
@ -1175,9 +1171,10 @@ static int patch_cxt5045(struct hda_codec *codec)
|
|||
|
||||
switch (codec->subsystem_id >> 16) {
|
||||
case 0x103c:
|
||||
/* HP laptop has a really bad sound over 0dB on NID 0x17.
|
||||
* Fix max PCM level to 0 dB
|
||||
* (originall it has 0x2b steps with 0dB offset 0x14)
|
||||
case 0x1734:
|
||||
/* HP & Fujitsu-Siemens laptops have really bad sound over 0dB
|
||||
* on NID 0x17. Fix max PCM level to 0 dB
|
||||
* (originally it has 0x2b steps with 0dB offset 0x14)
|
||||
*/
|
||||
snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
|
||||
(0x14 << AC_AMPCAP_OFFSET_SHIFT) |
|
||||
|
@ -1243,8 +1240,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
|
|||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int bits;
|
||||
|
||||
spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
spec->hp_present = snd_hda_jack_detect(codec, 0x13);
|
||||
|
||||
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
|
||||
/* See the note in cxt5047_hp_master_sw_put */
|
||||
|
@ -1267,8 +1263,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
|
|||
};
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x15, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x15);
|
||||
if (present)
|
||||
snd_hda_sequence_write(codec, mic_jack_on);
|
||||
else
|
||||
|
@ -1415,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
|
|||
.get = conexant_mux_enum_get,
|
||||
.put = conexant_mux_enum_put,
|
||||
},
|
||||
HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
|
||||
|
||||
{ } /* end */
|
||||
};
|
||||
|
@ -1621,9 +1607,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec)
|
|||
|
||||
if (spec->no_auto_mic)
|
||||
return;
|
||||
present = snd_hda_codec_read(codec, 0x17, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE;
|
||||
present = snd_hda_jack_detect(codec, 0x17);
|
||||
snd_hda_codec_write(codec, 0x14, 0,
|
||||
AC_VERB_SET_CONNECT_SEL,
|
||||
present ? 0x01 : 0x00);
|
||||
|
@ -1638,9 +1622,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
|
|||
|
||||
if (spec->no_auto_mic)
|
||||
return;
|
||||
present = snd_hda_codec_read(codec, 0x18, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE;
|
||||
present = snd_hda_jack_detect(codec, 0x18);
|
||||
if (present)
|
||||
spec->cur_adc_idx = 1;
|
||||
else
|
||||
|
@ -1661,9 +1643,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
|
|||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
|
||||
spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE;
|
||||
spec->hp_present = snd_hda_jack_detect(codec, 0x16);
|
||||
cxt5051_update_speaker(codec);
|
||||
}
|
||||
|
||||
|
@ -2011,8 +1991,47 @@ static void cxt5066_automic(struct hda_codec *codec)
|
|||
};
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x1a, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
present = snd_hda_jack_detect(codec, 0x1a);
|
||||
if (present) {
|
||||
snd_printdd("CXT5066: external microphone detected\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_present);
|
||||
} else {
|
||||
snd_printdd("CXT5066: external microphone absent\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_absent);
|
||||
}
|
||||
}
|
||||
|
||||
/* toggle input of built-in digital mic and mic jack appropriately */
|
||||
static void cxt5066_vostro_automic(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int present;
|
||||
|
||||
struct hda_verb ext_mic_present[] = {
|
||||
/* enable external mic, port B */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
|
||||
|
||||
/* switch to external mic input */
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
|
||||
/* disable internal digital mic */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
static struct hda_verb ext_mic_absent[] = {
|
||||
/* enable internal mic, port C */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
|
||||
/* switch to internal mic input */
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 2},
|
||||
|
||||
/* disable external mic, port B */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
|
||||
present = snd_hda_jack_detect(codec, 0x1a);
|
||||
if (present) {
|
||||
snd_printdd("CXT5066: external microphone detected\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_present);
|
||||
|
@ -2029,12 +2048,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
|
|||
unsigned int portA, portD;
|
||||
|
||||
/* Port A */
|
||||
portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE;
|
||||
portA = snd_hda_jack_detect(codec, 0x19);
|
||||
|
||||
/* Port D */
|
||||
portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE) << 1;
|
||||
portD = snd_hda_jack_detect(codec, 0x1c);
|
||||
|
||||
spec->hp_present = !!(portA | portD);
|
||||
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
|
||||
|
@ -2056,6 +2073,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
|
|||
}
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
|
||||
switch (res >> 26) {
|
||||
case CONEXANT_HP_EVENT:
|
||||
cxt5066_hp_automute(codec);
|
||||
break;
|
||||
case CONEXANT_MIC_EVENT:
|
||||
cxt5066_vostro_automic(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hda_input_mux cxt5066_analog_mic_boost = {
|
||||
.num_items = 5,
|
||||
.items = {
|
||||
|
@ -2297,6 +2328,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
|
|||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs_vostro[] = {
|
||||
/* Port A: headphones */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* Port B: external microphone */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port C: unused */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port D: unused */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port E: unused, but has primary EAPD */
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
|
||||
/* Port F: unused */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port G: internal speakers */
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* DAC1 */
|
||||
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* DAC2: unused */
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
|
||||
/* Digital microphone port */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
|
||||
/* Audio input selectors */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
|
||||
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
||||
|
||||
/* Disable SPDIF */
|
||||
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* enable unsolicited events for Port A and B */
|
||||
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
|
||||
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{ } /* end */
|
||||
|
@ -2318,6 +2410,7 @@ enum {
|
|||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_DELL_LAPTOP, /* Dell Laptop */
|
||||
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
|
||||
CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
|
||||
CXT5066_MODELS
|
||||
};
|
||||
|
||||
|
@ -2325,6 +2418,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
|
|||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_DELL_LAPTOP] = "dell-laptop",
|
||||
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
|
||||
[CXT5066_DELL_VOSTO] = "dell-vostro"
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
||||
|
@ -2333,6 +2427,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
|
||||
CXT5066_DELL_LAPTOP),
|
||||
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
|
||||
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2397,6 +2492,19 @@ static int patch_cxt5066(struct hda_codec *codec)
|
|||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
break;
|
||||
case CXT5066_DELL_VOSTO:
|
||||
codec->patch_ops.unsol_event = cxt5066_vostro_event;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
spec->port_d_mode = 0;
|
||||
|
||||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
break;
|
||||
|
@ -2417,6 +2525,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
|
|||
.patch = patch_cxt5051 },
|
||||
{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
|
||||
.patch = patch_cxt5066 },
|
||||
{ .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
|
||||
.patch = patch_cxt5066 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -2424,6 +2534,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045");
|
|||
MODULE_ALIAS("snd-hda-codec-id:14f15047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15066");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15067");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
|
|
@ -33,15 +33,41 @@
|
|||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
static hda_nid_t cvt_nid; /* audio converter */
|
||||
static hda_nid_t pin_nid; /* HDMI output pin */
|
||||
/*
|
||||
* The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
|
||||
* could support two independent pipes, each of them can be connected to one or
|
||||
* more ports (DVI, HDMI or DisplayPort).
|
||||
*
|
||||
* The HDA correspondence of pipes/ports are converter/pin nodes.
|
||||
*/
|
||||
#define INTEL_HDMI_CVTS 2
|
||||
#define INTEL_HDMI_PINS 3
|
||||
|
||||
#define INTEL_HDMI_EVENT_TAG 0x08
|
||||
static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
|
||||
"INTEL HDMI 0",
|
||||
"INTEL HDMI 1",
|
||||
};
|
||||
|
||||
struct intel_hdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
struct hda_pcm pcm_rec;
|
||||
struct hdmi_eld sink_eld;
|
||||
int num_cvts;
|
||||
int num_pins;
|
||||
hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */
|
||||
hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */
|
||||
|
||||
/*
|
||||
* source connection for each pin
|
||||
*/
|
||||
hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
|
||||
|
||||
/*
|
||||
* HDMI sink attached to each pin
|
||||
*/
|
||||
struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
|
||||
|
||||
/*
|
||||
* export one pcm per pipe
|
||||
*/
|
||||
struct hda_pcm pcm_rec[INTEL_HDMI_CVTS];
|
||||
};
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
|
@ -184,40 +210,186 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
|
|||
{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* HDA/HDMI auto parsing
|
||||
*/
|
||||
|
||||
static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nids[i]; i++)
|
||||
if (nids[i] == nid)
|
||||
return i;
|
||||
|
||||
snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
|
||||
int conn_len, curr;
|
||||
int index;
|
||||
|
||||
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: pin %d wcaps %#x "
|
||||
"does not support connection list\n",
|
||||
pin_nid, get_wcaps(codec, pin_nid));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
|
||||
HDA_MAX_CONNECTIONS);
|
||||
if (conn_len > 1)
|
||||
curr = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_CONNECT_SEL, 0);
|
||||
else
|
||||
curr = 0;
|
||||
|
||||
index = hda_node_index(spec->pin, pin_nid);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spec->pin_cvt[index] = conn_list[curr];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
int present = snd_hda_pin_sense(codec, pin_nid);
|
||||
|
||||
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
|
||||
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
|
||||
|
||||
if (present & AC_PINSENSE_ELDV)
|
||||
hdmi_get_show_eld(codec, pin_nid, eld);
|
||||
}
|
||||
|
||||
static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_pins >= INTEL_HDMI_PINS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for pin %d \n", pin_nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
|
||||
|
||||
spec->pin[spec->num_pins] = pin_nid;
|
||||
spec->num_pins++;
|
||||
|
||||
/*
|
||||
* It is assumed that converter nodes come first in the node list and
|
||||
* hence have been registered and usable now.
|
||||
*/
|
||||
return intel_hdmi_read_pin_conn(codec, pin_nid);
|
||||
}
|
||||
|
||||
static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_cvts >= INTEL_HDMI_CVTS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for converter %d \n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spec->cvt[spec->num_cvts] = nid;
|
||||
spec->num_cvts++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_parse_codec(struct hda_codec *codec)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, nodes;
|
||||
|
||||
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
|
||||
if (!nid || nodes < 0) {
|
||||
snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
case AC_WID_AUD_OUT:
|
||||
if (intel_hdmi_add_cvt(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case AC_WID_PIN:
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
if (!(caps & AC_PINCAP_HDMI))
|
||||
continue;
|
||||
if (intel_hdmi_add_pin(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* HDMI routines
|
||||
*/
|
||||
|
||||
#ifdef BE_PARANOID
|
||||
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
|
||||
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int *packet_index, int *byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_INDEX, 0);
|
||||
|
||||
*packet_index = val >> 5;
|
||||
*byte_index = val & 0x1f;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
|
||||
static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
|
||||
static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
unsigned char val)
|
||||
{
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
|
||||
}
|
||||
|
||||
static void hdmi_enable_output(struct hda_codec *codec)
|
||||
static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
/* Unmute */
|
||||
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
|
||||
|
@ -231,7 +403,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
|
|||
/*
|
||||
* Enable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_start_infoframe_trans(struct hda_codec *codec)
|
||||
static void hdmi_start_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
|
@ -241,59 +414,49 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
|
|||
/*
|
||||
* Disable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
|
||||
static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_DISABLE);
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec)
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
return 1 + snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CVT_CHAN_COUNT, 0);
|
||||
}
|
||||
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec,
|
||||
hda_nid_t nid, int chs)
|
||||
{
|
||||
snd_hda_codec_write(codec, cvt_nid, 0,
|
||||
if (chs != hdmi_get_channel_count(codec, nid))
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
||||
|
||||
if (chs != hdmi_get_channel_count(codec))
|
||||
snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
|
||||
chs, hdmi_get_channel_count(codec));
|
||||
}
|
||||
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec)
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
slot = snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
slot = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0x7);
|
||||
slot >> 4, slot & 0xf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_parse_eld(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->sink_eld;
|
||||
|
||||
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Audio InfoFrame routines
|
||||
*/
|
||||
|
||||
static void hdmi_debug_dip_size(struct hda_codec *codec)
|
||||
static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
|
@ -310,7 +473,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_clear_dip_buffers(struct hda_codec *codec)
|
||||
static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef BE_PARANOID
|
||||
int i, j;
|
||||
|
@ -339,23 +502,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *params = (u8 *)ai;
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 sum = 0;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec);
|
||||
hdmi_clear_dip_buffers(codec); /* be paranoid */
|
||||
ai->checksum = 0;
|
||||
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
sum += bytes[i];
|
||||
|
||||
for (i = 0; i < sizeof(ai); i++)
|
||||
sum += params[i];
|
||||
ai->checksum = - sum;
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec, pin_nid);
|
||||
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
|
||||
|
||||
hdmi_checksum_audio_infoframe(ai);
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(ai); i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, params[i]);
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -386,11 +561,11 @@ static void init_channel_allocations(void)
|
|||
*
|
||||
* TODO: it could select the wrong CA from multiple candidates.
|
||||
*/
|
||||
static int hdmi_setup_channel_allocation(struct hda_codec *codec,
|
||||
static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->sink_eld;
|
||||
struct hdmi_eld *eld;
|
||||
int i;
|
||||
int spk_mask = 0;
|
||||
int channels = 1 + (ai->CC02_CT47 & 0x7);
|
||||
|
@ -402,6 +577,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
|
|||
if (channels <= 2)
|
||||
return 0;
|
||||
|
||||
i = hda_node_index(spec->pin_cvt, nid);
|
||||
if (i < 0)
|
||||
return 0;
|
||||
eld = &spec->sink_eld[i];
|
||||
|
||||
/*
|
||||
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
|
||||
* in console or for audio devices. Assume the highest speakers
|
||||
|
@ -439,7 +619,7 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
|
|||
return ai->CA;
|
||||
}
|
||||
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
int i;
|
||||
|
@ -453,17 +633,41 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
|||
*/
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
snd_hda_codec_write(codec, cvt_nid, 0,
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
(i << 4) | i);
|
||||
|
||||
hdmi_debug_channel_mapping(codec);
|
||||
hdmi_debug_channel_mapping(codec, nid);
|
||||
}
|
||||
|
||||
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 val;
|
||||
int i;
|
||||
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
||||
if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
|
||||
!= AC_DIPXMIT_BEST)
|
||||
return false;
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++) {
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_DATA, 0);
|
||||
if (val != bytes[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin_nid;
|
||||
int i;
|
||||
struct hdmi_audio_infoframe ai = {
|
||||
.type = 0x84,
|
||||
.ver = 0x01,
|
||||
|
@ -471,11 +675,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|||
.CC02_CT47 = substream->runtime->channels - 1,
|
||||
};
|
||||
|
||||
hdmi_setup_channel_allocation(codec, &ai);
|
||||
hdmi_setup_channel_mapping(codec, &ai);
|
||||
hdmi_setup_channel_allocation(codec, nid, &ai);
|
||||
hdmi_setup_channel_mapping(codec, nid, &ai);
|
||||
|
||||
hdmi_fill_audio_infoframe(codec, &ai);
|
||||
hdmi_start_infoframe_trans(codec);
|
||||
for (i = 0; i < spec->num_pins; i++) {
|
||||
if (spec->pin_cvt[i] != nid)
|
||||
continue;
|
||||
if (!spec->sink_eld[i].monitor_present)
|
||||
continue;
|
||||
|
||||
pin_nid = spec->pin[i];
|
||||
if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
|
||||
hdmi_stop_infoframe_trans(codec, pin_nid);
|
||||
hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
|
||||
hdmi_start_infoframe_trans(codec, pin_nid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -485,27 +700,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|||
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int pind = !!(res & AC_UNSOL_RES_PD);
|
||||
int eldv = !!(res & AC_UNSOL_RES_ELDV);
|
||||
int index;
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
|
||||
pind, eldv);
|
||||
"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
||||
tag, pind, eldv);
|
||||
|
||||
index = hda_node_index(spec->pin, tag);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
spec->sink_eld[index].monitor_present = pind;
|
||||
spec->sink_eld[index].eld_valid = eldv;
|
||||
|
||||
if (pind && eldv) {
|
||||
hdmi_parse_eld(codec);
|
||||
hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
|
||||
/* TODO: do real things about ELD */
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
|
||||
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
"HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
tag,
|
||||
subtag,
|
||||
cp_state,
|
||||
cp_ready);
|
||||
|
@ -520,10 +747,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|||
|
||||
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (tag != INTEL_HDMI_EVENT_TAG) {
|
||||
if (hda_node_index(spec->pin, tag) < 0) {
|
||||
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
}
|
||||
|
@ -538,24 +766,29 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
|||
* Callbacks
|
||||
*/
|
||||
|
||||
static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int tag;
|
||||
int fmt;
|
||||
|
||||
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
||||
}
|
||||
tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
|
||||
fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
|
||||
|
||||
static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
snd_printdd("hdmi_setup_stream: "
|
||||
"NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
|
||||
nid,
|
||||
tag == stream_tag ? "" : "new-",
|
||||
stream_tag,
|
||||
fmt == format ? "" : "new-",
|
||||
format);
|
||||
|
||||
hdmi_stop_infoframe_trans(codec);
|
||||
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
if (tag != stream_tag)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
|
||||
if (fmt != format)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
}
|
||||
|
||||
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -564,43 +797,53 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
hdmi_set_channel_count(codec, hinfo->nid,
|
||||
substream->runtime->channels);
|
||||
|
||||
snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||
format, substream);
|
||||
hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
|
||||
|
||||
hdmi_set_channel_count(codec, substream->runtime->channels);
|
||||
|
||||
hdmi_setup_audio_infoframe(codec, substream);
|
||||
hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hda_pcm_stream intel_hdmi_pcm_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.ops = {
|
||||
.open = intel_hdmi_playback_pcm_open,
|
||||
.close = intel_hdmi_playback_pcm_close,
|
||||
.prepare = intel_hdmi_playback_pcm_prepare
|
||||
.prepare = intel_hdmi_playback_pcm_prepare,
|
||||
.cleanup = intel_hdmi_playback_pcm_cleanup,
|
||||
},
|
||||
};
|
||||
|
||||
static int intel_hdmi_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
int i;
|
||||
|
||||
codec->num_pcms = 1;
|
||||
codec->num_pcms = spec->num_cvts;
|
||||
codec->pcm_info = info;
|
||||
|
||||
/* NID to query formats and rates and setup streams */
|
||||
intel_hdmi_pcm_playback.nid = cvt_nid;
|
||||
for (i = 0; i < codec->num_pcms; i++, info++) {
|
||||
unsigned int chans;
|
||||
|
||||
info->name = "INTEL HDMI";
|
||||
chans = get_wcaps(codec, spec->cvt[i]);
|
||||
chans = get_wcaps_channels(chans);
|
||||
|
||||
info->name = intel_hdmi_pcm_names[i];
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
|
||||
intel_hdmi_pcm_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -609,29 +852,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
|
|||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
for (i = 0; i < codec->num_pcms; i++) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
hdmi_enable_output(codec);
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
for (i = 0; spec->pin[i]; i++) {
|
||||
hdmi_enable_output(codec, spec->pin[i]);
|
||||
snd_hda_codec_write(codec, spec->pin[i], 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
|
||||
AC_USRSP_EN | spec->pin[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_hdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
|
||||
|
||||
snd_hda_eld_proc_free(codec, &spec->sink_eld);
|
||||
kfree(spec);
|
||||
}
|
||||
|
||||
|
@ -643,49 +896,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
|
|||
.unsol_event = intel_hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static int do_patch_intel_hdmi(struct hda_codec *codec)
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec;
|
||||
int i;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->multiout.dig_out_nid = cvt_nid;
|
||||
|
||||
codec->spec = spec;
|
||||
if (intel_hdmi_parse_codec(codec) < 0) {
|
||||
codec->spec = NULL;
|
||||
kfree(spec);
|
||||
return -EINVAL;
|
||||
}
|
||||
codec->patch_ops = intel_hdmi_patch_ops;
|
||||
|
||||
snd_hda_eld_proc_new(codec, &spec->sink_eld);
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
|
||||
|
||||
init_channel_allocations();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
cvt_nid = 0x02;
|
||||
pin_nid = 0x03;
|
||||
return do_patch_intel_hdmi(codec);
|
||||
}
|
||||
|
||||
static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
|
||||
{
|
||||
cvt_nid = 0x02;
|
||||
pin_nid = 0x04;
|
||||
return do_patch_intel_hdmi(codec);
|
||||
}
|
||||
|
||||
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
|
||||
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
|
||||
{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -93,6 +93,7 @@ enum {
|
|||
STAC_92HD83XXX_REF,
|
||||
STAC_92HD83XXX_PWR_REF,
|
||||
STAC_DELL_S14,
|
||||
STAC_92HD83XXX_HP,
|
||||
STAC_92HD83XXX_MODELS
|
||||
};
|
||||
|
||||
|
@ -1085,7 +1086,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
if (!spec->auto_mic && spec->num_dmuxes > 0 &&
|
||||
snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
|
||||
stac_dmux_mixer.count = spec->num_dmuxes;
|
||||
err = snd_hda_ctl_add(codec,
|
||||
err = snd_hda_ctl_add(codec, 0,
|
||||
snd_ctl_new1(&stac_dmux_mixer, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1101,7 +1102,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
spec->spdif_mute = 1;
|
||||
}
|
||||
stac_smux_mixer.count = spec->num_smuxes;
|
||||
err = snd_hda_ctl_add(codec,
|
||||
err = snd_hda_ctl_add(codec, 0,
|
||||
snd_ctl_new1(&stac_smux_mixer, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
|
|||
[STAC_92HD83XXX_REF] = "ref",
|
||||
[STAC_92HD83XXX_PWR_REF] = "mic-ref",
|
||||
[STAC_DELL_S14] = "dell-s14",
|
||||
[STAC_92HD83XXX_HP] = "hp",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
|
||||
|
@ -1634,6 +1636,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
|
|||
"DFI LanParty", STAC_92HD83XXX_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
|
||||
"unknown Dell", STAC_DELL_S14),
|
||||
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
|
||||
"HP", STAC_92HD83XXX_HP),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -2648,6 +2652,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
|
|||
enum {
|
||||
STAC_CTL_WIDGET_VOL,
|
||||
STAC_CTL_WIDGET_MUTE,
|
||||
STAC_CTL_WIDGET_MUTE_BEEP,
|
||||
STAC_CTL_WIDGET_MONO_MUX,
|
||||
STAC_CTL_WIDGET_HP_SWITCH,
|
||||
STAC_CTL_WIDGET_IO_SWITCH,
|
||||
|
@ -2658,6 +2663,7 @@ enum {
|
|||
static struct snd_kcontrol_new stac92xx_control_templates[] = {
|
||||
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
|
||||
HDA_CODEC_MUTE(NULL, 0, 0, 0),
|
||||
HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
|
||||
STAC_MONO_MUX,
|
||||
STAC_CODEC_HP_SWITCH(NULL),
|
||||
STAC_CODEC_IO_SWITCH(NULL, 0),
|
||||
|
@ -2669,7 +2675,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
|
|||
static struct snd_kcontrol_new *
|
||||
stac_control_new(struct sigmatel_spec *spec,
|
||||
struct snd_kcontrol_new *ktemp,
|
||||
const char *name)
|
||||
const char *name,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
struct snd_kcontrol_new *knew;
|
||||
|
||||
|
@ -2685,6 +2692,8 @@ stac_control_new(struct sigmatel_spec *spec,
|
|||
spec->kctls.alloced--;
|
||||
return NULL;
|
||||
}
|
||||
if (nid)
|
||||
knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
|
||||
return knew;
|
||||
}
|
||||
|
||||
|
@ -2693,7 +2702,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
|
|||
int idx, const char *name,
|
||||
unsigned long val)
|
||||
{
|
||||
struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
|
||||
struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
|
||||
get_amp_nid_(val));
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
knew->index = idx;
|
||||
|
@ -2764,7 +2774,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
|
|||
if (!spec->num_adcs || imux->num_items <= 1)
|
||||
return 0; /* no need for input source control */
|
||||
knew = stac_control_new(spec, &stac_input_src_temp,
|
||||
stac_input_src_temp.name);
|
||||
stac_input_src_temp.name, 0);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
knew->count = spec->num_adcs;
|
||||
|
@ -3221,11 +3231,14 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
|
|||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
|
||||
int err;
|
||||
int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
|
||||
|
||||
if (spec->anabeep_nid == nid)
|
||||
type = STAC_CTL_WIDGET_MUTE;
|
||||
|
||||
/* check for mute support for the the amp */
|
||||
if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
|
||||
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
|
||||
err = stac92xx_add_control(spec, type,
|
||||
"Beep Playback Switch",
|
||||
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
|
||||
if (err < 0)
|
||||
|
@ -3258,12 +3271,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
int enabled = !!ucontrol->value.integer.value[0];
|
||||
if (codec->beep->enabled != enabled) {
|
||||
codec->beep->enabled = enabled;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
|
||||
|
@ -3631,6 +3639,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
|
|||
}
|
||||
}
|
||||
|
||||
static int is_dual_headphones(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int i, valid_hps;
|
||||
|
||||
if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
|
||||
spec->autocfg.hp_outs <= 1)
|
||||
return 0;
|
||||
valid_hps = 0;
|
||||
for (i = 0; i < spec->autocfg.hp_outs; i++) {
|
||||
hda_nid_t nid = spec->autocfg.hp_pins[i];
|
||||
unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
|
||||
if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
|
||||
continue;
|
||||
valid_hps++;
|
||||
}
|
||||
return (valid_hps > 1);
|
||||
}
|
||||
|
||||
|
||||
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
@ -3647,8 +3675,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
|
|||
/* If we have no real line-out pin and multiple hp-outs, HPs should
|
||||
* be set up as multi-channel outputs.
|
||||
*/
|
||||
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
|
||||
spec->autocfg.hp_outs > 1) {
|
||||
if (is_dual_headphones(codec)) {
|
||||
/* Copy hp_outs to line_outs, backup line_outs in
|
||||
* speaker_outs so that the following routines can handle
|
||||
* HP pins as primary outputs.
|
||||
|
@ -4329,6 +4356,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
|
|||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
static void stac92xx_shutup(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int i;
|
||||
hda_nid_t nid;
|
||||
|
||||
/* reset each pin before powering down DAC/ADC to avoid click noise */
|
||||
nid = codec->start_nid;
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
unsigned int wid_type = get_wcaps_type(wcaps);
|
||||
if (wid_type == AC_WID_PIN)
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
}
|
||||
|
||||
if (spec->eapd_mask)
|
||||
stac_gpio_set(codec, spec->gpio_mask,
|
||||
spec->gpio_dir, spec->gpio_data &
|
||||
~spec->eapd_mask);
|
||||
}
|
||||
|
||||
static void stac92xx_free(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
@ -4336,6 +4385,7 @@ static void stac92xx_free(struct hda_codec *codec)
|
|||
if (! spec)
|
||||
return;
|
||||
|
||||
stac92xx_shutup(codec);
|
||||
stac92xx_free_jacks(codec);
|
||||
snd_array_free(&spec->events);
|
||||
|
||||
|
@ -4386,12 +4436,16 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
|
|||
pin_ctl & ~flag);
|
||||
}
|
||||
|
||||
static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
|
||||
static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
if (!nid)
|
||||
return 0;
|
||||
if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
|
||||
& (1 << 31))
|
||||
/* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT
|
||||
* codecs behave wrongly when SET_PIN_SENSE is triggered, although
|
||||
* the pincap gives TRIG_REQ bit.
|
||||
*/
|
||||
if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -4791,28 +4845,28 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
if (nid != 0x13)
|
||||
return 0;
|
||||
if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE)
|
||||
spec->gpio_data |= spec->gpio_led; /* mute LED on */
|
||||
else
|
||||
spec->gpio_data &= ~spec->gpio_led; /* mute LED off */
|
||||
stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int i;
|
||||
hda_nid_t nid;
|
||||
|
||||
/* reset each pin before powering down DAC/ADC to avoid click noise */
|
||||
nid = codec->start_nid;
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
unsigned int wid_type = get_wcaps_type(wcaps);
|
||||
if (wid_type == AC_WID_PIN)
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
}
|
||||
|
||||
if (spec->eapd_mask)
|
||||
stac_gpio_set(codec, spec->gpio_mask,
|
||||
spec->gpio_dir, spec->gpio_data &
|
||||
~spec->eapd_mask);
|
||||
stac92xx_shutup(codec);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -4827,6 +4881,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
|
|||
.suspend = stac92xx_suspend,
|
||||
.resume = stac92xx_resume,
|
||||
#endif
|
||||
.reboot_notify = stac92xx_shutup,
|
||||
};
|
||||
|
||||
static int patch_stac9200(struct hda_codec *codec)
|
||||
|
@ -5172,6 +5227,22 @@ again:
|
|||
break;
|
||||
}
|
||||
|
||||
codec->patch_ops = stac92xx_patch_ops;
|
||||
|
||||
if (spec->board_config == STAC_92HD83XXX_HP)
|
||||
spec->gpio_led = 0x01;
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (spec->gpio_led) {
|
||||
spec->gpio_mask |= spec->gpio_led;
|
||||
spec->gpio_dir |= spec->gpio_led;
|
||||
spec->gpio_data |= spec->gpio_led;
|
||||
/* register check_power_status callback. */
|
||||
codec->patch_ops.check_power_status =
|
||||
idt92hd83xxx_hp_check_power_status;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = stac92xx_parse_auto_config(codec, 0x1d, 0);
|
||||
if (!err) {
|
||||
if (spec->board_config < 0) {
|
||||
|
@ -5207,8 +5278,6 @@ again:
|
|||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, num_dacs);
|
||||
|
||||
codec->patch_ops = stac92xx_patch_ops;
|
||||
|
||||
codec->proc_widget_hook = stac92hd_proc_hook;
|
||||
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@
|
|||
|
||||
snd-ice17xx-ak4xxx-objs := ak4xxx.o
|
||||
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
|
||||
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
|
||||
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
|
||||
|
|
|
@ -298,6 +298,16 @@ static void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
|
|||
inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
|
||||
}
|
||||
|
||||
static unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice)
|
||||
{
|
||||
return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION);
|
||||
}
|
||||
|
||||
static unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice)
|
||||
{
|
||||
return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK);
|
||||
}
|
||||
|
||||
static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
|
||||
{
|
||||
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
|
||||
|
@ -2557,7 +2567,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
|
|||
mutex_init(&ice->i2c_mutex);
|
||||
mutex_init(&ice->open_mutex);
|
||||
ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
|
||||
ice->gpio.get_mask = snd_ice1712_get_gpio_mask;
|
||||
ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
|
||||
ice->gpio.get_dir = snd_ice1712_get_gpio_dir;
|
||||
ice->gpio.set_data = snd_ice1712_set_gpio_data;
|
||||
ice->gpio.get_data = snd_ice1712_get_gpio_data;
|
||||
|
||||
|
|
|
@ -359,7 +359,9 @@ struct snd_ice1712 {
|
|||
unsigned int saved[2]; /* for ewx_i2c */
|
||||
/* operators */
|
||||
void (*set_mask)(struct snd_ice1712 *ice, unsigned int data);
|
||||
unsigned int (*get_mask)(struct snd_ice1712 *ice);
|
||||
void (*set_dir)(struct snd_ice1712 *ice, unsigned int data);
|
||||
unsigned int (*get_dir)(struct snd_ice1712 *ice);
|
||||
void (*set_data)(struct snd_ice1712 *ice, unsigned int data);
|
||||
unsigned int (*get_data)(struct snd_ice1712 *ice);
|
||||
/* misc operators - move to another place? */
|
||||
|
@ -377,8 +379,11 @@ struct snd_ice1712 {
|
|||
unsigned int (*get_rate)(struct snd_ice1712 *ice);
|
||||
void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
|
||||
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
|
||||
void (*set_spdif_clock)(struct snd_ice1712 *ice);
|
||||
|
||||
int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
|
||||
int (*get_spdif_master_type)(struct snd_ice1712 *ice);
|
||||
char **ext_clock_names;
|
||||
int ext_clock_count;
|
||||
void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
|
||||
#ifdef CONFIG_PM
|
||||
int (*pm_suspend)(struct snd_ice1712 *);
|
||||
int (*pm_resume)(struct snd_ice1712 *);
|
||||
|
@ -399,6 +404,11 @@ static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned in
|
|||
ice->gpio.set_dir(ice, bits);
|
||||
}
|
||||
|
||||
static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice)
|
||||
{
|
||||
return ice->gpio.get_dir(ice);
|
||||
}
|
||||
|
||||
static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits)
|
||||
{
|
||||
ice->gpio.set_mask(ice, bits);
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "phase.h"
|
||||
#include "wtm.h"
|
||||
#include "se.h"
|
||||
#include "quartet.h"
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
|
||||
|
@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{"
|
|||
PHASE_DEVICE_DESC
|
||||
WTM_DEVICE_DESC
|
||||
SE_DEVICE_DESC
|
||||
QTET_DEVICE_DESC
|
||||
"{VIA,VT1720},"
|
||||
"{VIA,VT1724},"
|
||||
"{ICEnsemble,Generic ICE1724},"
|
||||
|
@ -104,6 +106,8 @@ static int PRO_RATE_LOCKED;
|
|||
static int PRO_RATE_RESET = 1;
|
||||
static unsigned int PRO_RATE_DEFAULT = 44100;
|
||||
|
||||
static char *ext_clock_names[1] = { "IEC958 In" };
|
||||
|
||||
/*
|
||||
* Basic I/O
|
||||
*/
|
||||
|
@ -118,9 +122,12 @@ static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
|
|||
return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* locking rate makes sense only for internal clock mode
|
||||
*/
|
||||
static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
|
||||
{
|
||||
return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
|
||||
return (!ice->is_spdif_master(ice)) && PRO_RATE_LOCKED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -196,6 +203,12 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
|
|||
inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */
|
||||
}
|
||||
|
||||
/* get gpio direction 0 = read, 1 = write */
|
||||
static unsigned int snd_vt1724_get_gpio_dir(struct snd_ice1712 *ice)
|
||||
{
|
||||
return inl(ICEREG1724(ice, GPIO_DIRECTION));
|
||||
}
|
||||
|
||||
/* set the gpio mask (0 = writable) */
|
||||
static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
|
||||
{
|
||||
|
@ -205,6 +218,17 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
|
|||
inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
|
||||
}
|
||||
|
||||
static unsigned int snd_vt1724_get_gpio_mask(struct snd_ice1712 *ice)
|
||||
{
|
||||
unsigned int mask;
|
||||
if (!ice->vt1720)
|
||||
mask = (unsigned int)inb(ICEREG1724(ice, GPIO_WRITE_MASK_22));
|
||||
else
|
||||
mask = 0;
|
||||
mask = (mask << 16) | inw(ICEREG1724(ice, GPIO_WRITE_MASK));
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
|
||||
{
|
||||
outw(data, ICEREG1724(ice, GPIO_DATA));
|
||||
|
@ -651,10 +675,15 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
|
||||
}
|
||||
if (!force && is_pro_rate_locked(ice)) {
|
||||
/* comparing required and current rate - makes sense for
|
||||
* internal clock only */
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return (rate == ice->cur_rate) ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
if (force || !ice->is_spdif_master(ice)) {
|
||||
/* force means the rate was switched by ucontrol, otherwise
|
||||
* setting clock rate for internal clock mode */
|
||||
old_rate = ice->get_rate(ice);
|
||||
if (force || (old_rate != rate))
|
||||
ice->set_rate(ice, rate);
|
||||
|
@ -662,6 +691,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ice->cur_rate = rate;
|
||||
|
||||
|
@ -1016,6 +1046,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
|
|||
VT1724_BUFFER_ALIGN);
|
||||
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
VT1724_BUFFER_ALIGN);
|
||||
if (ice->pro_open)
|
||||
ice->pro_open(ice, substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1034,6 +1066,8 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
|
|||
VT1724_BUFFER_ALIGN);
|
||||
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
VT1724_BUFFER_ALIGN);
|
||||
if (ice->pro_open)
|
||||
ice->pro_open(ice, substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1787,15 +1821,21 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
int hw_rates_count = ice->hw_rates->count;
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = ice->hw_rates->count + 1;
|
||||
|
||||
uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count;
|
||||
/* upper limit - keep at top */
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
|
||||
strcpy(uinfo->value.enumerated.name, "IEC958 Input");
|
||||
if (uinfo->value.enumerated.item >= hw_rates_count)
|
||||
/* ext_clock items */
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
ice->ext_clock_names[
|
||||
uinfo->value.enumerated.item - hw_rates_count]);
|
||||
else
|
||||
/* int clock items */
|
||||
sprintf(uinfo->value.enumerated.name, "%d",
|
||||
ice->hw_rates->list[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
|
@ -1809,7 +1849,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
|
|||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
if (ice->is_spdif_master(ice)) {
|
||||
ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
|
||||
ucontrol->value.enumerated.item[0] = ice->hw_rates->count +
|
||||
ice->get_spdif_master_type(ice);
|
||||
} else {
|
||||
rate = ice->get_rate(ice);
|
||||
ucontrol->value.enumerated.item[0] = 0;
|
||||
|
@ -1824,8 +1865,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stdclock_get_spdif_master_type(struct snd_ice1712 *ice)
|
||||
{
|
||||
/* standard external clock - only single type - SPDIF IN */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setting clock to external - SPDIF */
|
||||
static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
|
||||
static int stdclock_set_spdif_clock(struct snd_ice1712 *ice, int type)
|
||||
{
|
||||
unsigned char oval;
|
||||
unsigned char i2s_oval;
|
||||
|
@ -1834,27 +1881,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
|
|||
/* setting 256fs */
|
||||
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
|
||||
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int old_rate, new_rate;
|
||||
unsigned int item = ucontrol->value.enumerated.item[0];
|
||||
unsigned int spdif = ice->hw_rates->count;
|
||||
unsigned int first_ext_clock = ice->hw_rates->count;
|
||||
|
||||
if (item > spdif)
|
||||
if (item > first_ext_clock + ice->ext_clock_count - 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* if rate = 0 => external clock */
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
if (ice->is_spdif_master(ice))
|
||||
old_rate = 0;
|
||||
else
|
||||
old_rate = ice->get_rate(ice);
|
||||
if (item == spdif) {
|
||||
/* switching to external clock via SPDIF */
|
||||
ice->set_spdif_clock(ice);
|
||||
if (item >= first_ext_clock) {
|
||||
/* switching to external clock */
|
||||
ice->set_spdif_clock(ice, item - first_ext_clock);
|
||||
new_rate = 0;
|
||||
} else {
|
||||
/* internal on-card clock */
|
||||
|
@ -1866,7 +1916,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
|
||||
/* the first reset to the SPDIF master mode? */
|
||||
/* the first switch to the ext. clock mode? */
|
||||
if (old_rate != new_rate && !new_rate) {
|
||||
/* notify akm chips as well */
|
||||
unsigned int i;
|
||||
|
@ -2136,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
|
|||
snd_vt1724_phase_cards,
|
||||
snd_vt1724_wtm_cards,
|
||||
snd_vt1724_se_cards,
|
||||
snd_vt1724_qtet_cards,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -2434,7 +2485,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
|||
mutex_init(&ice->open_mutex);
|
||||
mutex_init(&ice->i2c_mutex);
|
||||
ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
|
||||
ice->gpio.get_mask = snd_vt1724_get_gpio_mask;
|
||||
ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
|
||||
ice->gpio.get_dir = snd_vt1724_get_gpio_dir;
|
||||
ice->gpio.set_data = snd_vt1724_set_gpio_data;
|
||||
ice->gpio.get_data = snd_vt1724_get_gpio_data;
|
||||
ice->card = card;
|
||||
|
@ -2522,6 +2575,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* field init before calling chip_init */
|
||||
ice->ext_clock_count = 0;
|
||||
|
||||
for (tbl = card_tables; *tbl; tbl++) {
|
||||
for (c = *tbl; c->subvendor; c++) {
|
||||
if (c->subvendor == ice->eeprom.subvendor) {
|
||||
|
@ -2560,6 +2616,13 @@ __found:
|
|||
ice->set_mclk = stdclock_set_mclk;
|
||||
if (!ice->set_spdif_clock)
|
||||
ice->set_spdif_clock = stdclock_set_spdif_clock;
|
||||
if (!ice->get_spdif_master_type)
|
||||
ice->get_spdif_master_type = stdclock_get_spdif_master_type;
|
||||
if (!ice->ext_clock_names)
|
||||
ice->ext_clock_names = ext_clock_names;
|
||||
if (!ice->ext_clock_count)
|
||||
ice->ext_clock_count = ARRAY_SIZE(ext_clock_names);
|
||||
|
||||
if (!ice->hw_rates)
|
||||
set_std_hw_rates(ice);
|
||||
|
||||
|
@ -2719,7 +2782,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
|
|||
|
||||
if (ice->pm_saved_is_spdif_master) {
|
||||
/* switching to external clock via SPDIF */
|
||||
ice->set_spdif_clock(ice);
|
||||
ice->set_spdif_clock(ice, 0);
|
||||
} else {
|
||||
/* internal on-card clock */
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
|
||||
|
|
|
@ -412,25 +412,6 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
|
|||
},
|
||||
};
|
||||
|
||||
|
||||
static void ak4358_proc_regs_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
|
||||
int reg, val;
|
||||
for (reg = 0; reg <= 0xf; reg++) {
|
||||
val = snd_akm4xxx_get(ice->akm, 0, reg);
|
||||
snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void ak4358_proc_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry))
|
||||
snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read);
|
||||
}
|
||||
|
||||
static char *slave_vols[] __devinitdata = {
|
||||
PCM_VOLUME,
|
||||
MONITOR_AN_IN_VOLUME,
|
||||
|
@ -496,8 +477,6 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
|
|||
/* only capture SPDIF over AK4114 */
|
||||
err = snd_ak4114_build(spec->ak4114, NULL,
|
||||
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
|
||||
|
||||
ak4358_proc_init(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
@ -575,13 +554,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
|
|||
}
|
||||
|
||||
/* setting clock to external - SPDIF */
|
||||
static void juli_set_spdif_clock(struct snd_ice1712 *ice)
|
||||
static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type)
|
||||
{
|
||||
unsigned int old;
|
||||
old = ice->gpio.get_data(ice);
|
||||
/* external clock (= 0), multiply 1x, 48kHz */
|
||||
ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
|
||||
GPIO_FREQ_48KHZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when ak4114 detects change in the input SPDIF stream */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
#ifndef __SOUND_QTET_H
|
||||
#define __SOUND_QTET_H
|
||||
|
||||
#define QTET_DEVICE_DESC "{Infrasonic,Quartet},"
|
||||
|
||||
#define VT1724_SUBDEVICE_QTET 0x30305349 /* Infrasonic Quartet */
|
||||
|
||||
extern struct snd_ice1712_card_info snd_vt1724_qtet_cards[];
|
||||
|
||||
#endif /* __SOUND_QTET_H */
|
|
@ -1,7 +1,8 @@
|
|||
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
|
||||
snd-hifier-objs := hifier.o
|
||||
snd-oxygen-objs := oxygen.o
|
||||
snd-virtuoso-objs := virtuoso.o
|
||||
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
|
||||
xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o
|
||||
|
||||
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
|
||||
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef CS2000_H_INCLUDED
|
||||
#define CS2000_H_INCLUDED
|
||||
|
||||
#define CS2000_DEV_ID 0x01
|
||||
#define CS2000_DEV_CTRL 0x02
|
||||
#define CS2000_DEV_CFG_1 0x03
|
||||
#define CS2000_DEV_CFG_2 0x04
|
||||
#define CS2000_GLOBAL_CFG 0x05
|
||||
#define CS2000_RATIO_0 0x06 /* 32 bits, big endian */
|
||||
#define CS2000_RATIO_1 0x0a
|
||||
#define CS2000_RATIO_2 0x0e
|
||||
#define CS2000_RATIO_3 0x12
|
||||
#define CS2000_FUN_CFG_1 0x16
|
||||
#define CS2000_FUN_CFG_2 0x17
|
||||
#define CS2000_FUN_CFG_3 0x1e
|
||||
|
||||
/* DEV_ID */
|
||||
#define CS2000_DEVICE_MASK 0xf8
|
||||
#define CS2000_REVISION_MASK 0x07
|
||||
|
||||
/* DEV_CTRL */
|
||||
#define CS2000_UNLOCK 0x80
|
||||
#define CS2000_AUX_OUT_DIS 0x02
|
||||
#define CS2000_CLK_OUT_DIS 0x01
|
||||
|
||||
/* DEV_CFG_1 */
|
||||
#define CS2000_R_MOD_SEL_MASK 0xe0
|
||||
#define CS2000_R_MOD_SEL_1 0x00
|
||||
#define CS2000_R_MOD_SEL_2 0x20
|
||||
#define CS2000_R_MOD_SEL_4 0x40
|
||||
#define CS2000_R_MOD_SEL_8 0x60
|
||||
#define CS2000_R_MOD_SEL_1_2 0x80
|
||||
#define CS2000_R_MOD_SEL_1_4 0xa0
|
||||
#define CS2000_R_MOD_SEL_1_8 0xc0
|
||||
#define CS2000_R_MOD_SEL_1_16 0xe0
|
||||
#define CS2000_R_SEL_MASK 0x18
|
||||
#define CS2000_R_SEL_SHIFT 3
|
||||
#define CS2000_AUX_OUT_SRC_MASK 0x06
|
||||
#define CS2000_AUX_OUT_SRC_REF_CLK 0x00
|
||||
#define CS2000_AUX_OUT_SRC_CLK_IN 0x02
|
||||
#define CS2000_AUX_OUT_SRC_CLK_OUT 0x04
|
||||
#define CS2000_AUX_OUT_SRC_PLL_LOCK 0x06
|
||||
#define CS2000_EN_DEV_CFG_1 0x01
|
||||
|
||||
/* DEV_CFG_2 */
|
||||
#define CS2000_LOCK_CLK_MASK 0x06
|
||||
#define CS2000_LOCK_CLK_SHIFT 1
|
||||
#define CS2000_FRAC_N_SRC_MASK 0x01
|
||||
#define CS2000_FRAC_N_SRC_STATIC 0x00
|
||||
#define CS2000_FRAC_N_SRC_DYNAMIC 0x01
|
||||
|
||||
/* GLOBAL_CFG */
|
||||
#define CS2000_FREEZE 0x08
|
||||
#define CS2000_EN_DEV_CFG_2 0x01
|
||||
|
||||
/* FUN_CFG_1 */
|
||||
#define CS2000_CLK_SKIP_EN 0x80
|
||||
#define CS2000_AUX_LOCK_CFG_MASK 0x40
|
||||
#define CS2000_AUX_LOCK_CFG_PP_HIGH 0x00
|
||||
#define CS2000_AUX_LOCK_CFG_OD_LOW 0x40
|
||||
#define CS2000_REF_CLK_DIV_MASK 0x18
|
||||
#define CS2000_REF_CLK_DIV_4 0x00
|
||||
#define CS2000_REF_CLK_DIV_2 0x08
|
||||
#define CS2000_REF_CLK_DIV_1 0x10
|
||||
|
||||
/* FUN_CFG_2 */
|
||||
#define CS2000_CLK_OUT_UNL 0x10
|
||||
#define CS2000_L_F_RATIO_CFG_MASK 0x08
|
||||
#define CS2000_L_F_RATIO_CFG_20_12 0x00
|
||||
#define CS2000_L_F_RATIO_CFG_12_20 0x08
|
||||
|
||||
/* FUN_CFG_3 */
|
||||
#define CS2000_CLK_IN_BW_MASK 0x70
|
||||
#define CS2000_CLK_IN_BW_1 0x00
|
||||
#define CS2000_CLK_IN_BW_2 0x10
|
||||
#define CS2000_CLK_IN_BW_4 0x20
|
||||
#define CS2000_CLK_IN_BW_8 0x30
|
||||
#define CS2000_CLK_IN_BW_16 0x40
|
||||
#define CS2000_CLK_IN_BW_32 0x50
|
||||
#define CS2000_CLK_IN_BW_64 0x60
|
||||
#define CS2000_CLK_IN_BW_128 0x70
|
||||
|
||||
#endif
|
|
@ -17,6 +17,12 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* CMI8788:
|
||||
*
|
||||
* SPI 0 -> AK4396
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <sound/control.h>
|
||||
|
@ -51,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = {
|
|||
MODULE_DEVICE_TABLE(pci, hifier_ids);
|
||||
|
||||
struct hifier_data {
|
||||
u8 ak4396_ctl2;
|
||||
u8 ak4396_regs[5];
|
||||
};
|
||||
|
||||
static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct hifier_data *data = chip->model_data;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CLOCK_160 |
|
||||
(0 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
AK4396_WRITE | (reg << 8) | value);
|
||||
data->ak4396_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
|
||||
ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
|
||||
struct hifier_data *data = chip->model_data;
|
||||
|
||||
if (value != data->ak4396_regs[reg])
|
||||
ak4396_write(chip, reg, value);
|
||||
}
|
||||
|
||||
static void hifier_registers_init(struct oxygen *chip)
|
||||
|
@ -75,16 +86,19 @@ static void hifier_registers_init(struct oxygen *chip)
|
|||
struct hifier_data *data = chip->model_data;
|
||||
|
||||
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
|
||||
ak4396_write(chip, AK4396_CONTROL_2,
|
||||
data->ak4396_regs[AK4396_CONTROL_2]);
|
||||
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
|
||||
update_ak4396_volume(chip);
|
||||
ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
|
||||
ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
|
||||
}
|
||||
|
||||
static void hifier_init(struct oxygen *chip)
|
||||
{
|
||||
struct hifier_data *data = chip->model_data;
|
||||
|
||||
data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
data->ak4396_regs[AK4396_CONTROL_2] =
|
||||
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
hifier_registers_init(chip);
|
||||
|
||||
snd_component_add(chip->card, "AK4396");
|
||||
|
@ -106,20 +120,29 @@ static void set_ak4396_params(struct oxygen *chip,
|
|||
struct hifier_data *data = chip->model_data;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
|
||||
value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
|
||||
if (params_rate(params) <= 54000)
|
||||
value |= AK4396_DFS_NORMAL;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value |= AK4396_DFS_DOUBLE;
|
||||
else
|
||||
value |= AK4396_DFS_QUAD;
|
||||
data->ak4396_ctl2 = value;
|
||||
|
||||
msleep(1); /* wait for the new MCLK to become stable */
|
||||
|
||||
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
|
||||
if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
|
||||
ak4396_write(chip, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB);
|
||||
ak4396_write(chip, AK4396_CONTROL_2, value);
|
||||
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
|
||||
ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
|
||||
}
|
||||
|
||||
static void update_ak4396_mute(struct oxygen *chip)
|
||||
|
@ -127,11 +150,10 @@ static void update_ak4396_mute(struct oxygen *chip)
|
|||
struct hifier_data *data = chip->model_data;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_ctl2 & ~AK4396_SMUTE;
|
||||
value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
|
||||
if (chip->dac_mute)
|
||||
value |= AK4396_SMUTE;
|
||||
data->ak4396_ctl2 = value;
|
||||
ak4396_write(chip, AK4396_CONTROL_2, value);
|
||||
ak4396_write_cached(chip, AK4396_CONTROL_2, value);
|
||||
}
|
||||
|
||||
static void set_cs5340_params(struct oxygen *chip,
|
||||
|
@ -141,21 +163,14 @@ static void set_cs5340_params(struct oxygen *chip,
|
|||
|
||||
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
|
||||
|
||||
static int hifier_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strcmp(template->name, "Stereo Upmixing"))
|
||||
return 1; /* stereo only - we don't need upmixing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_hifier = {
|
||||
.shortname = "C-Media CMI8787",
|
||||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8788",
|
||||
.init = hifier_init,
|
||||
.control_filter = hifier_control_filter,
|
||||
.cleanup = hifier_cleanup,
|
||||
.resume = hifier_resume,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_ak4396_params,
|
||||
.set_adc_params = set_cs5340_params,
|
||||
.update_dac_volume = update_ak4396_volume,
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* CMI8788:
|
||||
*
|
||||
* SPI 0 -> 1st AK4396 (front)
|
||||
* SPI 1 -> 2nd AK4396 (surround)
|
||||
* SPI 2 -> 3rd AK4396 (center/LFE)
|
||||
|
@ -27,6 +29,10 @@
|
|||
* GPIO 0 -> DFS0 of AK5385
|
||||
* GPIO 1 -> DFS1 of AK5385
|
||||
* GPIO 8 -> enable headphone amplifier on HT-Omega models
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
@ -91,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
|
|||
#define GPIO_CLARO_HP 0x0100
|
||||
|
||||
struct generic_data {
|
||||
u8 ak4396_ctl2;
|
||||
u16 saved_wm8785_registers[2];
|
||||
u8 ak4396_regs[4][5];
|
||||
u16 wm8785_regs[3];
|
||||
};
|
||||
|
||||
static void ak4396_write(struct oxygen *chip, unsigned int codec,
|
||||
|
@ -102,12 +108,24 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec,
|
|||
static const u8 codec_spi_map[4] = {
|
||||
0, 1, 2, 4
|
||||
};
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CLOCK_160 |
|
||||
(codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
AK4396_WRITE | (reg << 8) | value);
|
||||
data->ak4396_regs[codec][reg] = value;
|
||||
}
|
||||
|
||||
static void ak4396_write_cached(struct oxygen *chip, unsigned int codec,
|
||||
u8 reg, u8 value)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
if (value != data->ak4396_regs[codec][reg])
|
||||
ak4396_write(chip, codec, reg, value);
|
||||
}
|
||||
|
||||
static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
|
||||
|
@ -120,20 +138,8 @@ static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
|
|||
(3 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
|
||||
(reg << 9) | value);
|
||||
if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
|
||||
data->saved_wm8785_registers[reg] = value;
|
||||
}
|
||||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ak4396_write(chip, i,
|
||||
AK4396_LCH_ATT, chip->dac_volume[i * 2]);
|
||||
ak4396_write(chip, i,
|
||||
AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
|
||||
}
|
||||
if (reg < ARRAY_SIZE(data->wm8785_regs))
|
||||
data->wm8785_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void ak4396_registers_init(struct oxygen *chip)
|
||||
|
@ -142,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip)
|
|||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_2, data->ak4396_ctl2);
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_3, AK4396_PCM);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2,
|
||||
data->ak4396_regs[0][AK4396_CONTROL_2]);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_3,
|
||||
AK4396_PCM);
|
||||
ak4396_write(chip, i, AK4396_LCH_ATT,
|
||||
chip->dac_volume[i * 2]);
|
||||
ak4396_write(chip, i, AK4396_RCH_ATT,
|
||||
chip->dac_volume[i * 2 + 1]);
|
||||
}
|
||||
update_ak4396_volume(chip);
|
||||
}
|
||||
|
||||
static void ak4396_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
data->ak4396_regs[0][AK4396_CONTROL_2] =
|
||||
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
ak4396_registers_init(chip);
|
||||
snd_component_add(chip->card, "AK4396");
|
||||
}
|
||||
|
@ -173,17 +183,17 @@ static void wm8785_registers_init(struct oxygen *chip)
|
|||
struct generic_data *data = chip->model_data;
|
||||
|
||||
wm8785_write(chip, WM8785_R7, 0);
|
||||
wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
|
||||
wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
|
||||
wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
|
||||
wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
|
||||
}
|
||||
|
||||
static void wm8785_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
|
||||
WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
|
||||
data->saved_wm8785_registers[1] = WM8785_WL_24;
|
||||
data->wm8785_regs[0] =
|
||||
WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
|
||||
data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
|
||||
wm8785_registers_init(chip);
|
||||
snd_component_add(chip->card, "WM8785");
|
||||
}
|
||||
|
@ -264,24 +274,36 @@ static void set_ak4396_params(struct oxygen *chip,
|
|||
unsigned int i;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
|
||||
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
|
||||
if (params_rate(params) <= 54000)
|
||||
value |= AK4396_DFS_NORMAL;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value |= AK4396_DFS_DOUBLE;
|
||||
else
|
||||
value |= AK4396_DFS_QUAD;
|
||||
data->ak4396_ctl2 = value;
|
||||
|
||||
msleep(1); /* wait for the new MCLK to become stable */
|
||||
|
||||
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_1, AK4396_DIF_24_MSB);
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_2, value);
|
||||
ak4396_write(chip, i,
|
||||
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, value);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ak4396_write_cached(chip, i, AK4396_LCH_ATT,
|
||||
chip->dac_volume[i * 2]);
|
||||
ak4396_write_cached(chip, i, AK4396_RCH_ATT,
|
||||
chip->dac_volume[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,21 +313,19 @@ static void update_ak4396_mute(struct oxygen *chip)
|
|||
unsigned int i;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_ctl2 & ~AK4396_SMUTE;
|
||||
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
|
||||
if (chip->dac_mute)
|
||||
value |= AK4396_SMUTE;
|
||||
data->ak4396_ctl2 = value;
|
||||
for (i = 0; i < 4; ++i)
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, value);
|
||||
ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
|
||||
}
|
||||
|
||||
static void set_wm8785_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int value;
|
||||
|
||||
wm8785_write(chip, WM8785_R7, 0);
|
||||
|
||||
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
|
||||
if (params_rate(params) <= 48000)
|
||||
value |= WM8785_OSR_SINGLE;
|
||||
|
@ -313,13 +333,11 @@ static void set_wm8785_params(struct oxygen *chip,
|
|||
value |= WM8785_OSR_DOUBLE;
|
||||
else
|
||||
value |= WM8785_OSR_QUAD;
|
||||
if (value != data->wm8785_regs[0]) {
|
||||
wm8785_write(chip, WM8785_R7, 0);
|
||||
wm8785_write(chip, WM8785_R0, value);
|
||||
|
||||
if (snd_pcm_format_width(params_format(params)) <= 16)
|
||||
value = WM8785_WL_16;
|
||||
else
|
||||
value = WM8785_WL_24;
|
||||
wm8785_write(chip, WM8785_R1, value);
|
||||
wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_ak5385_params(struct oxygen *chip,
|
||||
|
@ -337,6 +355,134 @@ static void set_ak5385_params(struct oxygen *chip,
|
|||
value, GPIO_AK5385_DFS_MASK);
|
||||
}
|
||||
|
||||
static int rolloff_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"Sharp Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
int changed;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->ak4396_regs[0][AK4396_CONTROL_2];
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= AK4396_SLOW;
|
||||
else
|
||||
reg &= ~AK4396_SLOW;
|
||||
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
|
||||
if (changed) {
|
||||
for (i = 0; i < 4; ++i)
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new rolloff_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DAC Filter Playback Enum",
|
||||
.info = rolloff_info,
|
||||
.get = rolloff_get,
|
||||
.put = rolloff_put,
|
||||
};
|
||||
|
||||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"None", "High-pass Filter"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= WM8785_HPFR | WM8785_HPFL;
|
||||
changed = reg != data->wm8785_regs[WM8785_R2];
|
||||
if (changed)
|
||||
wm8785_write(chip, WM8785_R2, reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new hpf_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
};
|
||||
|
||||
static int generic_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
}
|
||||
|
||||
static int generic_wm8785_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
|
||||
|
||||
static const struct oxygen_model model_generic = {
|
||||
|
@ -344,8 +490,10 @@ static const struct oxygen_model model_generic = {
|
|||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8788",
|
||||
.init = generic_init,
|
||||
.mixer_init = generic_wm8785_mixer_init,
|
||||
.cleanup = generic_cleanup,
|
||||
.resume = generic_resume,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_ak4396_params,
|
||||
.set_adc_params = set_wm8785_params,
|
||||
.update_dac_volume = update_ak4396_volume,
|
||||
|
@ -374,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
|
|||
switch (id->driver_data) {
|
||||
case MODEL_MERIDIAN:
|
||||
chip->model.init = meridian_init;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.resume = meridian_resume;
|
||||
chip->model.set_adc_params = set_ak5385_params;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
|
@ -389,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
|
|||
break;
|
||||
case MODEL_CLARO_HALO:
|
||||
chip->model.init = claro_halo_init;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.cleanup = claro_cleanup;
|
||||
chip->model.suspend = claro_suspend;
|
||||
chip->model.resume = claro_resume;
|
||||
|
|
|
@ -78,12 +78,15 @@ struct oxygen_model {
|
|||
void (*resume)(struct oxygen *chip);
|
||||
void (*pcm_hardware_filter)(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware);
|
||||
unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params);
|
||||
void (*set_dac_params)(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void (*set_adc_params)(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void (*update_dac_volume)(struct oxygen *chip);
|
||||
void (*update_dac_mute)(struct oxygen *chip);
|
||||
void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed);
|
||||
void (*gpio_changed)(struct oxygen *chip);
|
||||
void (*uart_input)(struct oxygen *chip);
|
||||
void (*ac97_switch)(struct oxygen *chip,
|
||||
|
@ -162,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip);
|
|||
/* oxygen_pcm.c */
|
||||
|
||||
int oxygen_pcm_init(struct oxygen *chip);
|
||||
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params);
|
||||
|
||||
/* oxygen_io.c */
|
||||
|
||||
|
|
|
@ -278,7 +278,11 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
|
|||
static void oxygen_restore_eeprom(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
|
||||
u16 eeprom_id;
|
||||
|
||||
eeprom_id = oxygen_read_eeprom(chip, 0);
|
||||
if (eeprom_id != OXYGEN_EEPROM_ID &&
|
||||
(eeprom_id != 0xffff || id->subdevice != 0x8788)) {
|
||||
/*
|
||||
* This function gets called only when a known card model has
|
||||
* been detected, i.e., we know there is a valid subsystem
|
||||
|
@ -303,6 +307,28 @@ static void oxygen_restore_eeprom(struct oxygen *chip,
|
|||
}
|
||||
}
|
||||
|
||||
static void pci_bridge_magic(void)
|
||||
{
|
||||
struct pci_dev *pci = NULL;
|
||||
u32 tmp;
|
||||
|
||||
for (;;) {
|
||||
/* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
|
||||
pci = pci_get_device(0x12d8, 0xe110, pci);
|
||||
if (!pci)
|
||||
break;
|
||||
/*
|
||||
* ... configure its secondary internal arbiter to park to
|
||||
* the secondary port, instead of to the last master.
|
||||
*/
|
||||
if (!pci_read_config_dword(pci, 0x40, &tmp)) {
|
||||
tmp |= 1;
|
||||
pci_write_config_dword(pci, 0x40, tmp);
|
||||
}
|
||||
/* Why? Try asking C-Media. */
|
||||
}
|
||||
}
|
||||
|
||||
static void oxygen_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -581,6 +607,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
|||
snd_card_set_dev(card, &pci->dev);
|
||||
card->private_free = oxygen_card_free;
|
||||
|
||||
pci_bridge_magic();
|
||||
oxygen_init(chip);
|
||||
chip->model.init(chip);
|
||||
|
||||
|
|
|
@ -99,11 +99,15 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
|
|||
|
||||
static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[3] = {
|
||||
"Front", "Front+Surround", "Front+Surround+Back"
|
||||
static const char *const names[5] = {
|
||||
"Front",
|
||||
"Front+Surround",
|
||||
"Front+Surround+Back",
|
||||
"Front+Surround+Center/LFE",
|
||||
"Front+Surround+Center/LFE+Back",
|
||||
};
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
unsigned int count = 2 + (chip->model.dac_channels == 8);
|
||||
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
|
@ -127,7 +131,7 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
|||
void oxygen_update_dac_routing(struct oxygen *chip)
|
||||
{
|
||||
/* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */
|
||||
static const unsigned int reg_values[3] = {
|
||||
static const unsigned int reg_values[5] = {
|
||||
/* stereo -> front */
|
||||
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
|
||||
(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
|
@ -143,6 +147,16 @@ void oxygen_update_dac_routing(struct oxygen *chip)
|
|||
(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
|
||||
/* stereo -> front+surround+center/LFE */
|
||||
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
|
||||
(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
|
||||
/* stereo -> front+surround+center/LFE+back */
|
||||
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
|
||||
};
|
||||
u8 channels;
|
||||
unsigned int reg_value;
|
||||
|
@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip)
|
|||
OXYGEN_PLAY_DAC1_SOURCE_MASK |
|
||||
OXYGEN_PLAY_DAC2_SOURCE_MASK |
|
||||
OXYGEN_PLAY_DAC3_SOURCE_MASK);
|
||||
if (chip->model.update_center_lfe_mix)
|
||||
chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
|
||||
}
|
||||
|
||||
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
unsigned int count = 2 + (chip->model.dac_channels == 8);
|
||||
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
|
||||
int changed;
|
||||
|
||||
if (value->value.enumerated.item[0] >= count)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != chip->dac_routing;
|
||||
if (changed) {
|
||||
chip->dac_routing = min(value->value.enumerated.item[0],
|
||||
count - 1);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->dac_routing = value->value.enumerated.item[0];
|
||||
oxygen_update_dac_routing(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
|
@ -790,7 +805,7 @@ static const struct {
|
|||
.controls = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Switch",
|
||||
.name = "Analog Input Monitor Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = monitor_get,
|
||||
.put = monitor_put,
|
||||
|
@ -798,7 +813,7 @@ static const struct {
|
|||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Volume",
|
||||
.name = "Analog Input Monitor Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = monitor_volume_info,
|
||||
|
@ -815,7 +830,7 @@ static const struct {
|
|||
.controls = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Switch",
|
||||
.name = "Analog Input Monitor Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = monitor_get,
|
||||
.put = monitor_put,
|
||||
|
@ -823,7 +838,7 @@ static const struct {
|
|||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Volume",
|
||||
.name = "Analog Input Monitor Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = monitor_volume_info,
|
||||
|
@ -840,7 +855,7 @@ static const struct {
|
|||
.controls = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Switch",
|
||||
.name = "Analog Input Monitor Playback Switch",
|
||||
.index = 1,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = monitor_get,
|
||||
|
@ -849,7 +864,7 @@ static const struct {
|
|||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Input Monitor Volume",
|
||||
.name = "Analog Input Monitor Playback Volume",
|
||||
.index = 1,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
|
@ -867,7 +882,7 @@ static const struct {
|
|||
.controls = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Digital Input Monitor Switch",
|
||||
.name = "Digital Input Monitor Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = monitor_get,
|
||||
.put = monitor_put,
|
||||
|
@ -875,7 +890,7 @@ static const struct {
|
|||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Digital Input Monitor Volume",
|
||||
.name = "Digital Input Monitor Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = monitor_volume_info,
|
||||
|
@ -954,6 +969,9 @@ static int add_controls(struct oxygen *chip,
|
|||
if (err == 1)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(template.name, "Stereo Upmixing") &&
|
||||
chip->model.dac_channels == 2)
|
||||
continue;
|
||||
if (!strcmp(template.name, "Master Playback Volume") &&
|
||||
chip->model.dac_tlv) {
|
||||
template.tlv.p = chip->model.dac_tlv;
|
||||
|
|
|
@ -271,13 +271,16 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
|
||||
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
|
||||
unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
if (params_rate(hw_params) <= 96000)
|
||||
return OXYGEN_I2S_MCLK_256;
|
||||
else
|
||||
return OXYGEN_I2S_MCLK_128;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_default_i2s_mclk);
|
||||
|
||||
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
|
@ -354,7 +357,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
|
|||
OXYGEN_REC_FORMAT_A_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
oxygen_i2s_mclk(hw_params) |
|
||||
chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
|
@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
|
|||
if (!is_ac97)
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
oxygen_i2s_mclk(hw_params) |
|
||||
chip->model.get_i2s_mclk(chip, PCM_B,
|
||||
hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
|
@ -435,6 +439,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_OUT_ENABLE);
|
||||
|
@ -446,6 +451,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
|
|||
OXYGEN_SPDIF_OUT_RATE_MASK);
|
||||
oxygen_update_spdif_source(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -459,6 +465,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
|
||||
oxygen_play_channels(hw_params),
|
||||
|
@ -469,18 +476,18 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
|
|||
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.dac_i2s_format |
|
||||
oxygen_i2s_mclk(hw_params) |
|
||||
chip->model.get_i2s_mclk(chip, PCM_MULTICH,
|
||||
hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
OXYGEN_I2S_MCLK_MASK |
|
||||
OXYGEN_I2S_BITS_MASK);
|
||||
oxygen_update_dac_routing(chip);
|
||||
oxygen_update_spdif_source(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
chip->model.set_dac_params(chip, hw_params);
|
||||
oxygen_update_dac_routing(chip);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
#ifndef XONAR_H_INCLUDED
|
||||
#define XONAR_H_INCLUDED
|
||||
|
||||
#include "oxygen.h"
|
||||
|
||||
struct xonar_generic {
|
||||
unsigned int anti_pop_delay;
|
||||
u16 output_enable_bit;
|
||||
u8 ext_power_reg;
|
||||
u8 ext_power_int_reg;
|
||||
u8 ext_power_bit;
|
||||
u8 has_power;
|
||||
};
|
||||
|
||||
struct xonar_hdmi {
|
||||
u8 params[5];
|
||||
};
|
||||
|
||||
/* generic helper functions */
|
||||
|
||||
void xonar_enable_output(struct oxygen *chip);
|
||||
void xonar_disable_output(struct oxygen *chip);
|
||||
void xonar_init_ext_power(struct oxygen *chip);
|
||||
void xonar_init_cs53x1(struct oxygen *chip);
|
||||
void xonar_set_cs53x1_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value);
|
||||
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value);
|
||||
|
||||
/* model-specific card drivers */
|
||||
|
||||
int get_xonar_pcm179x_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id);
|
||||
int get_xonar_cs43xx_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id);
|
||||
|
||||
/* HDMI helper functions */
|
||||
|
||||
void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data);
|
||||
void xonar_hdmi_cleanup(struct oxygen *chip);
|
||||
void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi);
|
||||
void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware);
|
||||
void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void xonar_hdmi_uart_input(struct oxygen *chip);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,434 @@
|
|||
/*
|
||||
* card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xonar D1/DX
|
||||
* -----------
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> CS4398 (front)
|
||||
* <-> CS4362A (surround, center/LFE, back)
|
||||
*
|
||||
* GPI 0 <- external power present (DX only)
|
||||
*
|
||||
* GPIO 0 -> enable output to speakers
|
||||
* GPIO 1 -> enable front panel I/O
|
||||
* GPIO 2 -> M0 of CS5361
|
||||
* GPIO 3 -> M1 of CS5361
|
||||
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
|
||||
*
|
||||
* CS4398:
|
||||
*
|
||||
* AD0 <- 1
|
||||
* AD1 <- 1
|
||||
*
|
||||
* CS4362A:
|
||||
*
|
||||
* AD0 <- 0
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "xonar.h"
|
||||
#include "cs4398.h"
|
||||
#include "cs4362a.h"
|
||||
|
||||
#define GPI_EXT_POWER 0x01
|
||||
#define GPIO_D1_OUTPUT_ENABLE 0x0001
|
||||
#define GPIO_D1_FRONT_PANEL 0x0002
|
||||
#define GPIO_D1_INPUT_ROUTE 0x0100
|
||||
|
||||
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
|
||||
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
|
||||
|
||||
struct xonar_cs43xx {
|
||||
struct xonar_generic generic;
|
||||
u8 cs4398_regs[8];
|
||||
u8 cs4362a_regs[15];
|
||||
};
|
||||
|
||||
static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
|
||||
if (reg < ARRAY_SIZE(data->cs4398_regs))
|
||||
data->cs4398_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
if (value != data->cs4398_regs[reg])
|
||||
cs4398_write(chip, reg, value);
|
||||
}
|
||||
|
||||
static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
|
||||
if (reg < ARRAY_SIZE(data->cs4362a_regs))
|
||||
data->cs4362a_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
if (value != data->cs4362a_regs[reg])
|
||||
cs4362a_write(chip, reg, value);
|
||||
}
|
||||
|
||||
static void cs43xx_registers_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
/* set CPEN (control port mode) and power down */
|
||||
cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
|
||||
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
|
||||
/* configure */
|
||||
cs4398_write(chip, 2, data->cs4398_regs[2]);
|
||||
cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
|
||||
cs4398_write(chip, 4, data->cs4398_regs[4]);
|
||||
cs4398_write(chip, 5, data->cs4398_regs[5]);
|
||||
cs4398_write(chip, 6, data->cs4398_regs[6]);
|
||||
cs4398_write(chip, 7, data->cs4398_regs[7]);
|
||||
cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
|
||||
cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
|
||||
CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
|
||||
cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]);
|
||||
cs4362a_write(chip, 0x05, 0);
|
||||
for (i = 6; i <= 14; ++i)
|
||||
cs4362a_write(chip, i, data->cs4362a_regs[i]);
|
||||
/* clear power down */
|
||||
cs4398_write(chip, 8, CS4398_CPEN);
|
||||
cs4362a_write(chip, 0x01, CS4362A_CPEN);
|
||||
}
|
||||
|
||||
static void xonar_d1_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
data->generic.anti_pop_delay = 800;
|
||||
data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE;
|
||||
data->cs4398_regs[2] =
|
||||
CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
|
||||
data->cs4398_regs[4] = CS4398_MUTEP_LOW |
|
||||
CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE;
|
||||
data->cs4398_regs[5] = 60 * 2;
|
||||
data->cs4398_regs[6] = 60 * 2;
|
||||
data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP |
|
||||
CS4398_ZERO_CROSS | CS4398_SOFT_RAMP;
|
||||
data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE;
|
||||
data->cs4362a_regs[6] = CS4362A_FM_SINGLE |
|
||||
CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
|
||||
data->cs4362a_regs[7] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[8] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[9] = data->cs4362a_regs[6];
|
||||
data->cs4362a_regs[10] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[11] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[12] = data->cs4362a_regs[6];
|
||||
data->cs4362a_regs[13] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[14] = 60 | CS4362A_MUTE;
|
||||
|
||||
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||
OXYGEN_2WIRE_LENGTH_8 |
|
||||
OXYGEN_2WIRE_INTERRUPT_MASK |
|
||||
OXYGEN_2WIRE_SPEED_FAST);
|
||||
|
||||
cs43xx_registers_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
|
||||
|
||||
xonar_init_cs53x1(chip);
|
||||
xonar_enable_output(chip);
|
||||
|
||||
snd_component_add(chip->card, "CS4398");
|
||||
snd_component_add(chip->card, "CS4362A");
|
||||
snd_component_add(chip->card, "CS5361");
|
||||
}
|
||||
|
||||
static void xonar_dx_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
data->generic.ext_power_reg = OXYGEN_GPI_DATA;
|
||||
data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
|
||||
data->generic.ext_power_bit = GPI_EXT_POWER;
|
||||
xonar_init_ext_power(chip);
|
||||
xonar_d1_init(chip);
|
||||
}
|
||||
|
||||
static void xonar_d1_cleanup(struct oxygen *chip)
|
||||
{
|
||||
xonar_disable_output(chip);
|
||||
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
|
||||
oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
|
||||
}
|
||||
|
||||
static void xonar_d1_suspend(struct oxygen *chip)
|
||||
{
|
||||
xonar_d1_cleanup(chip);
|
||||
}
|
||||
|
||||
static void xonar_d1_resume(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
|
||||
msleep(1);
|
||||
cs43xx_registers_init(chip);
|
||||
xonar_enable_output(chip);
|
||||
}
|
||||
|
||||
static void set_cs43xx_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
u8 cs4398_fm, cs4362a_fm;
|
||||
|
||||
if (params_rate(params) <= 50000) {
|
||||
cs4398_fm = CS4398_FM_SINGLE;
|
||||
cs4362a_fm = CS4362A_FM_SINGLE;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
cs4398_fm = CS4398_FM_DOUBLE;
|
||||
cs4362a_fm = CS4362A_FM_DOUBLE;
|
||||
} else {
|
||||
cs4398_fm = CS4398_FM_QUAD;
|
||||
cs4362a_fm = CS4362A_FM_QUAD;
|
||||
}
|
||||
cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST;
|
||||
cs4398_write_cached(chip, 2, cs4398_fm);
|
||||
cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK;
|
||||
cs4362a_write_cached(chip, 6, cs4362a_fm);
|
||||
cs4362a_write_cached(chip, 12, cs4362a_fm);
|
||||
cs4362a_fm &= CS4362A_FM_MASK;
|
||||
cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK;
|
||||
cs4362a_write_cached(chip, 9, cs4362a_fm);
|
||||
}
|
||||
|
||||
static void update_cs4362a_volumes(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 mute;
|
||||
|
||||
mute = chip->dac_mute ? CS4362A_MUTE : 0;
|
||||
for (i = 0; i < 6; ++i)
|
||||
cs4362a_write_cached(chip, 7 + i + i / 2,
|
||||
(127 - chip->dac_volume[2 + i]) | mute);
|
||||
}
|
||||
|
||||
static void update_cs43xx_volume(struct oxygen *chip)
|
||||
{
|
||||
cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2);
|
||||
cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2);
|
||||
update_cs4362a_volumes(chip);
|
||||
}
|
||||
|
||||
static void update_cs43xx_mute(struct oxygen *chip)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
|
||||
if (chip->dac_mute)
|
||||
reg |= CS4398_MUTE_B | CS4398_MUTE_A;
|
||||
cs4398_write_cached(chip, 4, reg);
|
||||
update_cs4362a_volumes(chip);
|
||||
}
|
||||
|
||||
static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
u8 reg;
|
||||
|
||||
reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK;
|
||||
if (mixed)
|
||||
reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR;
|
||||
else
|
||||
reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
|
||||
cs4362a_write_cached(chip, 9, reg);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new front_panel_switch = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Front Panel Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = xonar_gpio_bit_switch_get,
|
||||
.put = xonar_gpio_bit_switch_put,
|
||||
.private_value = GPIO_D1_FRONT_PANEL,
|
||||
};
|
||||
|
||||
static int rolloff_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"Fast Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->cs4398_regs[7] & CS4398_FILT_SEL) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
int changed;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->cs4398_regs[7];
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= CS4398_FILT_SEL;
|
||||
else
|
||||
reg &= ~CS4398_FILT_SEL;
|
||||
changed = reg != data->cs4398_regs[7];
|
||||
if (changed) {
|
||||
cs4398_write(chip, 7, reg);
|
||||
if (reg & CS4398_FILT_SEL)
|
||||
reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL;
|
||||
else
|
||||
reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
|
||||
cs4362a_write(chip, 0x04, reg);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new rolloff_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DAC Filter Playback Enum",
|
||||
.info = rolloff_info,
|
||||
.get = rolloff_get,
|
||||
.put = rolloff_put,
|
||||
};
|
||||
|
||||
static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int mute)
|
||||
{
|
||||
if (reg == AC97_LINE) {
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
mute ? GPIO_D1_INPUT_ROUTE : 0,
|
||||
GPIO_D1_INPUT_ROUTE);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
|
||||
|
||||
static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strncmp(template->name, "CD Capture ", 11))
|
||||
return 1; /* no CD input */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xonar_d1_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_xonar_d1 = {
|
||||
.longname = "Asus Virtuoso 100",
|
||||
.chip = "AV200",
|
||||
.init = xonar_d1_init,
|
||||
.control_filter = xonar_d1_control_filter,
|
||||
.mixer_init = xonar_d1_mixer_init,
|
||||
.cleanup = xonar_d1_cleanup,
|
||||
.suspend = xonar_d1_suspend,
|
||||
.resume = xonar_d1_resume,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_cs43xx_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_cs43xx_volume,
|
||||
.update_dac_mute = update_cs43xx_mute,
|
||||
.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
|
||||
.ac97_switch = xonar_d1_line_mic_ac97_switch,
|
||||
.dac_tlv = cs4362a_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_cs43xx),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2,
|
||||
.dac_channels = 8,
|
||||
.dac_volume_min = 127 - 60,
|
||||
.dac_volume_max = 127,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
int __devinit get_xonar_cs43xx_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
switch (id->subdevice) {
|
||||
case 0x834f:
|
||||
chip->model = model_xonar_d1;
|
||||
chip->model.shortname = "Xonar D1";
|
||||
break;
|
||||
case 0x8275:
|
||||
case 0x8327:
|
||||
chip->model = model_xonar_d1;
|
||||
chip->model.shortname = "Xonar DX";
|
||||
chip->model.init = xonar_dx_init;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* helper functions for HDMI models (Xonar HDAV1.3)
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "xonar.h"
|
||||
|
||||
static void hdmi_write_command(struct oxygen *chip, u8 command,
|
||||
unsigned int count, const u8 *params)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 checksum;
|
||||
|
||||
oxygen_write_uart(chip, 0xfb);
|
||||
oxygen_write_uart(chip, 0xef);
|
||||
oxygen_write_uart(chip, command);
|
||||
oxygen_write_uart(chip, count);
|
||||
for (i = 0; i < count; ++i)
|
||||
oxygen_write_uart(chip, params[i]);
|
||||
checksum = 0xfb + 0xef + command + count;
|
||||
for (i = 0; i < count; ++i)
|
||||
checksum += params[i];
|
||||
oxygen_write_uart(chip, checksum);
|
||||
}
|
||||
|
||||
static void xonar_hdmi_init_commands(struct oxygen *chip,
|
||||
struct xonar_hdmi *hdmi)
|
||||
{
|
||||
u8 param;
|
||||
|
||||
oxygen_reset_uart(chip);
|
||||
param = 0;
|
||||
hdmi_write_command(chip, 0x61, 1, ¶m);
|
||||
param = 1;
|
||||
hdmi_write_command(chip, 0x74, 1, ¶m);
|
||||
hdmi_write_command(chip, 0x54, 5, hdmi->params);
|
||||
}
|
||||
|
||||
void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi)
|
||||
{
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_48000;
|
||||
hdmi->params[4] = 1;
|
||||
xonar_hdmi_init_commands(chip, hdmi);
|
||||
}
|
||||
|
||||
void xonar_hdmi_cleanup(struct oxygen *chip)
|
||||
{
|
||||
u8 param = 0;
|
||||
|
||||
hdmi_write_command(chip, 0x74, 1, ¶m);
|
||||
}
|
||||
|
||||
void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi)
|
||||
{
|
||||
xonar_hdmi_init_commands(chip, hdmi);
|
||||
}
|
||||
|
||||
void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware)
|
||||
{
|
||||
if (channel == PCM_MULTICH) {
|
||||
hardware->rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_192000;
|
||||
hardware->rate_min = 44100;
|
||||
}
|
||||
}
|
||||
|
||||
void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
hdmi->params[0] = 0; /* 1 = non-audio */
|
||||
switch (params_rate(params)) {
|
||||
case 44100:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_44100;
|
||||
break;
|
||||
case 48000:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_48000;
|
||||
break;
|
||||
default: /* 96000 */
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_96000;
|
||||
break;
|
||||
case 192000:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_192000;
|
||||
break;
|
||||
}
|
||||
hdmi->params[2] = params_channels(params) / 2 - 1;
|
||||
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
|
||||
hdmi->params[3] = 0;
|
||||
else
|
||||
hdmi->params[3] = 0xc0;
|
||||
hdmi->params[4] = 1; /* ? */
|
||||
hdmi_write_command(chip, 0x54, 5, hdmi->params);
|
||||
}
|
||||
|
||||
void xonar_hdmi_uart_input(struct oxygen *chip)
|
||||
{
|
||||
if (chip->uart_input_count >= 2 &&
|
||||
chip->uart_input[chip->uart_input_count - 2] == 'O' &&
|
||||
chip->uart_input[chip->uart_input_count - 1] == 'K') {
|
||||
printk(KERN_DEBUG "message from HDMI chip received:\n");
|
||||
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
|
||||
chip->uart_input, chip->uart_input_count);
|
||||
chip->uart_input_count = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* helper functions for Asus Xonar cards
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "xonar.h"
|
||||
|
||||
|
||||
#define GPIO_CS53x1_M_MASK 0x000c
|
||||
#define GPIO_CS53x1_M_SINGLE 0x0000
|
||||
#define GPIO_CS53x1_M_DOUBLE 0x0004
|
||||
#define GPIO_CS53x1_M_QUAD 0x0008
|
||||
|
||||
|
||||
void xonar_enable_output(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
|
||||
msleep(data->anti_pop_delay);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
|
||||
}
|
||||
|
||||
void xonar_disable_output(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
|
||||
}
|
||||
|
||||
static void xonar_ext_power_gpio_changed(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
u8 has_power;
|
||||
|
||||
has_power = !!(oxygen_read8(chip, data->ext_power_reg)
|
||||
& data->ext_power_bit);
|
||||
if (has_power != data->has_power) {
|
||||
data->has_power = has_power;
|
||||
if (has_power) {
|
||||
snd_printk(KERN_NOTICE "power restored\n");
|
||||
} else {
|
||||
snd_printk(KERN_CRIT
|
||||
"Hey! Don't unplug the power cable!\n");
|
||||
/* TODO: stop PCMs */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xonar_init_ext_power(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_set_bits8(chip, data->ext_power_int_reg,
|
||||
data->ext_power_bit);
|
||||
chip->interrupt_mask |= OXYGEN_INT_GPIO;
|
||||
chip->model.gpio_changed = xonar_ext_power_gpio_changed;
|
||||
data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
|
||||
& data->ext_power_bit);
|
||||
}
|
||||
|
||||
void xonar_init_cs53x1(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
|
||||
}
|
||||
|
||||
void xonar_set_cs53x1_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
if (params_rate(params) <= 54000)
|
||||
value = GPIO_CS53x1_M_SINGLE;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value = GPIO_CS53x1_M_DOUBLE;
|
||||
else
|
||||
value = GPIO_CS53x1_M_QUAD;
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
value, GPIO_CS53x1_M_MASK);
|
||||
}
|
||||
|
||||
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
|
||||
value->value.integer.value[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
u16 old_bits, new_bits;
|
||||
int changed;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
if (value->value.integer.value[0])
|
||||
new_bits = old_bits | bit;
|
||||
else
|
||||
new_bits = old_bits & ~bit;
|
||||
changed = new_bits != old_bits;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return changed;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue