Merge branch 'topic/hda' into for-linus

This commit is contained in:
Takashi Iwai 2009-12-04 16:22:45 +01:00
commit 86e1d57e4f
48 changed files with 9553 additions and 2224 deletions

View file

@ -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 setup before initializing the codecs. This option is
available only when CONFIG_SND_HDA_PATCH_LOADER=y is set. available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
See HD-Audio.txt for details. 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 (global) options]
single_cmd - Use single immediate commands to communicate with single_cmd - Use single immediate commands to communicate with

View file

@ -391,6 +391,7 @@ STAC92HD83*
ref Reference board ref Reference board
mic-ref Reference board with power management for ports mic-ref Reference board with power management for ports
dell-s14 Dell laptop dell-s14 Dell laptop
hp HP laptops with (inverted) mute-LED
auto BIOS setup (default) auto BIOS setup (default)
STAC9872 STAC9872

321
include/sound/ak4113.h Normal file
View file

@ -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 */

View file

@ -95,13 +95,13 @@
/* AK4114_REG_IO0 */ /* AK4114_REG_IO0 */
#define AK4114_TX1E (1<<7) /* TX1 Output Enable (1 = enable) */ #define AK4114_TX1E (1<<7) /* TX1 Output Enable (1 = enable) */
#define AK4114_OPS12 (1<<2) /* Output Though Data Selector for TX1 pin */ #define AK4114_OPS12 (1<<6) /* Output Data Selector for TX1 pin */
#define AK4114_OPS11 (1<<1) /* Output Though Data Selector for TX1 pin */ #define AK4114_OPS11 (1<<5) /* Output Data Selector for TX1 pin */
#define AK4114_OPS10 (1<<0) /* Output Though 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_TX0E (1<<3) /* TX0 Output Enable (1 = enable) */
#define AK4114_OPS02 (1<<2) /* Output Though Data Selector for TX0 pin */ #define AK4114_OPS02 (1<<2) /* Output Data Selector for TX0 pin */
#define AK4114_OPS01 (1<<1) /* Output Though Data Selector for TX0 pin */ #define AK4114_OPS01 (1<<1) /* Output Data Selector for TX0 pin */
#define AK4114_OPS00 (1<<0) /* Output Though Data Selector for TX0 pin */ #define AK4114_OPS00 (1<<0) /* Output Data Selector for TX0 pin */
/* AK4114_REG_IO1 */ /* AK4114_REG_IO1 */
#define AK4114_EFH1 (1<<7) /* Interrupt 0 pin Hold */ #define AK4114_EFH1 (1<<7) /* Interrupt 0 pin Hold */

View file

@ -68,7 +68,7 @@ struct snd_akm4xxx {
enum { enum {
SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4524, SND_AK4528, SND_AK4529,
SND_AK4355, SND_AK4358, SND_AK4381, SND_AK4355, SND_AK4358, SND_AK4381,
SND_AK5365 SND_AK5365, SND_AK4620,
} type; } type;
/* (array) information of combined codecs */ /* (array) information of combined codecs */
@ -76,6 +76,9 @@ struct snd_akm4xxx {
const struct snd_akm4xxx_adc_channel *adc_info; const struct snd_akm4xxx_adc_channel *adc_info;
struct snd_ak4xxx_ops ops; 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, void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,

View file

@ -5,6 +5,7 @@
snd-ak4114-objs := ak4114.o snd-ak4114-objs := ak4114.o
snd-ak4117-objs := ak4117.o snd-ak4117-objs := ak4117.o
snd-ak4113-objs := ak4113.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o snd-ak4xxx-adda-objs := ak4xxx-adda.o
snd-pt2258-objs := pt2258.o snd-pt2258-objs := pt2258.o
snd-tea575x-tuner-objs := tea575x-tuner.o snd-tea575x-tuner-objs := tea575x-tuner.o
@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
# Module Dependency # Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.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 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o

639
sound/i2c/other/ak4113.c Normal file
View file

@ -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);
}

View file

@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
*/ */
#include <asm/io.h> #include <asm/io.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -29,6 +29,7 @@
#include <sound/control.h> #include <sound/control.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include <sound/ak4xxx-adda.h> #include <sound/ak4xxx-adda.h>
#include <sound/info.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters"); 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) static void ak4524_reset(struct snd_akm4xxx *ak, int state)
{ {
unsigned int chip; 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++) { for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
if (state) if (state)
continue; continue;
/* DAC volumes */ /* DAC volumes */
for (reg = 0x04; reg < maxreg; reg++) for (reg = 0x04; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg)); snd_akm4xxx_get(ak, chip, reg));
} }
} }
/* reset procedure for AK4355 and AK4358 */ /* reset procedure for AK4355 and AK4358 */
static void ak435X_reset(struct snd_akm4xxx *ak, int state, static void ak435X_reset(struct snd_akm4xxx *ak, int state)
unsigned char total_regs)
{ {
unsigned char reg; 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 */ snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return; return;
} }
for (reg = 0x00; reg < total_regs; reg++) for (reg = 0x00; reg < ak->total_regs; reg++)
if (reg != 0x01) if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(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 int chip;
unsigned char reg; unsigned char reg;
for (chip = 0; chip < ak->num_dacs/2; chip++) { for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
if (state) if (state)
continue; continue;
for (reg = 0x01; reg < 0x05; reg++) for (reg = 0x01; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(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) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
case SND_AK4528: case SND_AK4528:
case SND_AK4620:
ak4524_reset(ak, state); ak4524_reset(ak, state);
break; break;
case SND_AK4529: case SND_AK4529:
/* FIXME: needed for ak4529? */ /* FIXME: needed for ak4529? */
break; break;
case SND_AK4355: case SND_AK4355:
ak435X_reset(ak, state, 0x0b); ak435X_reset(ak, state);
break; break;
case SND_AK4358: case SND_AK4358:
ak435X_reset(ak, state, 0x10); ak435X_reset(ak, state);
break; break;
case SND_AK4381: case SND_AK4381:
ak4381_reset(ak, state); ak4381_reset(ak, state);
@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
* Volume conversion table for non-linear volumes * Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB * 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 * AK5365 input attenuation
*/ */
static const unsigned char vol_cvt_datt[128] = { 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 */ 0x00, 0x0f, /* 0: power-up, un-reset */
0xff, 0xff 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; const unsigned char *ptr, *inits;
unsigned char reg, data; unsigned char reg, data;
@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
switch (ak->type) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
inits = inits_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; break;
case SND_AK4528: case SND_AK4528:
inits = inits_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; break;
case SND_AK4529: case SND_AK4529:
inits = inits_ak4529; inits = inits_ak4529;
num_chips = 1; ak->num_chips = 1;
ak->name = "ak4529";
ak->total_regs = 0x0d;
break; break;
case SND_AK4355: case SND_AK4355:
inits = inits_ak4355; inits = inits_ak4355;
num_chips = 1; ak->num_chips = 1;
ak->name = "ak4355";
ak->total_regs = 0x0b;
break; break;
case SND_AK4358: case SND_AK4358:
inits = inits_ak4358; inits = inits_ak4358;
num_chips = 1; ak->num_chips = 1;
ak->name = "ak4358";
ak->total_regs = 0x10;
break; break;
case SND_AK4381: case SND_AK4381:
inits = inits_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; break;
case SND_AK5365: case SND_AK5365:
/* FIXME: any init sequence? */ /* FIXME: any init sequence? */
ak->num_chips = 1;
ak->name = "ak5365";
ak->total_regs = 0x08;
return; return;
case SND_AK4620:
inits = inits_ak4620;
ak->num_chips = ak->num_dacs / 2;
ak->name = "ak4620";
ak->total_regs = 0x08;
break;
default: default:
snd_BUG(); snd_BUG();
return; return;
} }
for (chip = 0; chip < num_chips; chip++) { for (chip = 0; chip < ak->num_chips; chip++) {
ptr = inits; ptr = inits;
while (*ptr != 0xff) { while (*ptr != 0xff) {
reg = *ptr++; reg = *ptr++;
data = *ptr++; data = *ptr++;
snd_akm4xxx_write(ak, chip, reg, data); 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); AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
knew.tlv.p = db_scale_linear; knew.tlv.p = db_scale_linear;
break; 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: default:
return -EINVAL; return -EINVAL;
} }
@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
static int build_adc_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; struct snd_kcontrol_new knew;
mixer_ch = 0; mixer_ch = 0;
if (ak->type == SND_AK4528)
return 0; /* no controls */
for (idx = 0; idx < ak->num_adcs;) { for (idx = 0; idx < ak->num_adcs;) {
memset(&knew, 0, sizeof(knew)); memset(&knew, 0, sizeof(knew));
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) { if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
} }
/* register 4 & 5 */ /* register 4 & 5 */
if (ak->type == SND_AK5365) if (ak->type == SND_AK5365)
knew.private_value = max_steps = 152;
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
AK_VOL_CVT | AK_IPGA;
else else
knew.private_value = max_steps = 164;
AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) | knew.private_value =
AK_VOL_CVT | AK_IPGA; AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
AK_VOL_CVT | AK_IPGA;
knew.tlv.p = db_scale_vol_datt; knew.tlv.p = db_scale_vol_datt;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0) if (err < 0)
@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
switch (ak->type) { switch (ak->type) {
case SND_AK4524: case SND_AK4524:
case SND_AK4528: case SND_AK4528:
case SND_AK4620:
/* register 3 */ /* register 3 */
knew.private_value = AK_COMPOSE(idx, 3, 0, 0); knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break; break;
@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0; 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 snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{ {
int err, num_emphs; int err, num_emphs;
@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
err = build_adc_controls(ak); err = build_adc_controls(ak);
if (err < 0) if (err < 0)
return err; return err;
if (ak->type == SND_AK4355 || ak->type == SND_AK4358) if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1; num_emphs = 1;
else if (ak->type == SND_AK4620)
num_emphs = 0;
else else
num_emphs = ak->num_dacs / 2; num_emphs = ak->num_dacs / 2;
err = build_deemphasis(ak, num_emphs); err = build_deemphasis(ak, num_emphs);
if (err < 0)
return err;
err = proc_init(ak);
if (err < 0) if (err < 0)
return err; return err;
return 0; return 0;
} }
EXPORT_SYMBOL(snd_akm4xxx_build_controls); EXPORT_SYMBOL(snd_akm4xxx_build_controls);
static int __init alsa_akm4xxx_module_init(void) static int __init alsa_akm4xxx_module_init(void)

View file

@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
Say Y here to build a digital beep interface for HD-audio Say Y here to build a digital beep interface for HD-audio
driver. This interface is used to generate digital beeps. 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 config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer" 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 select SND_JACK
help help
Say Y here to enable the jack plugging notification via Say Y here to enable the jack plugging notification via

View file

@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0; 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 input_dev *input_dev;
struct hda_beep *beep; struct hda_codec *codec = beep->codec;
int err; 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(); input_dev = input_allocate_device();
if (!input_dev) { if (!input_dev) {
kfree(beep); printk(KERN_INFO "hda_beep: unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
} }
@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
err = input_register_device(input_dev); err = input_register_device(input_dev);
if (err < 0) { if (err < 0) {
input_free_device(input_dev); input_free_device(input_dev);
kfree(beep); printk(KERN_INFO "hda_beep: unable to register input device\n");
return err; 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 */ /* enable linear scale */
snd_hda_codec_write(codec, nid, 0, snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01); AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid; beep->nid = nid;
beep->dev = input_dev;
beep->codec = codec; beep->codec = codec;
beep->enabled = 1; beep->mode = codec->beep_mode;
codec->beep = beep; 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); 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; return 0;
} }
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); 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; struct hda_beep *beep = codec->beep;
if (beep) { if (beep) {
cancel_work_sync(&beep->beep_work); cancel_work_sync(&beep->register_work);
cancel_delayed_work(&beep->unregister_work);
input_unregister_device(beep->dev); if (beep->enabled)
kfree(beep); snd_hda_do_detach(beep);
codec->beep = NULL; codec->beep = NULL;
kfree(beep);
} }
} }
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);

View file

@ -24,19 +24,29 @@
#include "hda_codec.h" #include "hda_codec.h"
#define HDA_BEEP_MODE_OFF 0
#define HDA_BEEP_MODE_ON 1
#define HDA_BEEP_MODE_SWREG 2
/* beep information */ /* beep information */
struct hda_beep { struct hda_beep {
struct input_dev *dev; struct input_dev *dev;
struct hda_codec *codec; struct hda_codec *codec;
unsigned int mode;
char phys[32]; char phys[32];
int tone; int tone;
hda_nid_t nid; hda_nid_t nid;
unsigned int enabled:1; unsigned int enabled:1;
unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ 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 work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
}; };
#ifdef CONFIG_SND_HDA_INPUT_BEEP #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); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec); void snd_hda_detach_beep_device(struct hda_codec *codec);
#else #else

File diff suppressed because it is too large Load diff

View file

@ -286,6 +286,10 @@ enum {
#define AC_PWRST_D1SUP (1<<1) #define AC_PWRST_D1SUP (1<<1)
#define AC_PWRST_D2SUP (1<<2) #define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3) #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 */ /* Power state values */
#define AC_PWRST_SETTING (0xf<<0) #define AC_PWRST_SETTING (0xf<<0)
@ -674,6 +678,7 @@ struct hda_codec_ops {
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif #endif
void (*reboot_notify)(struct hda_codec *codec);
}; };
/* record for amp information cache */ /* record for amp information cache */
@ -771,6 +776,7 @@ struct hda_codec {
/* beep device */ /* beep device */
struct hda_beep *beep; struct hda_beep *beep;
unsigned int beep_mode;
/* widget capabilities cache */ /* widget capabilities cache */
unsigned int num_nodes; unsigned int num_nodes;
@ -811,6 +817,9 @@ struct hda_codec {
unsigned int power_transition :1; /* power-state in transition */ unsigned int power_transition :1; /* power-state in transition */
int power_count; /* current (global) power refcount */ int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */ struct delayed_work power_work; /* delayed task for powerdown */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
#endif #endif
/* codec-specific additional proc output */ /* codec-specific additional proc output */
@ -910,6 +919,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
* Misc * Misc
*/ */
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); 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 * 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_up(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec);
#define snd_hda_codec_needs_resume(codec) codec->power_count #define snd_hda_codec_needs_resume(codec) codec->power_count
void snd_hda_update_power_acct(struct hda_codec *codec);
#else #else
static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_up(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {}

View file

@ -309,17 +309,12 @@ out_fail:
return -EINVAL; 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) static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
{ {
int eldv; int eldv;
int present; int present;
present = hdmi_present_sense(codec, nid); present = snd_hda_pin_sense(codec, nid);
eldv = (present & AC_PINSENSE_ELDV); eldv = (present & AC_PINSENSE_ELDV);
present = (present & AC_PINSENSE_PRESENCE); present = (present & AC_PINSENSE_PRESENCE);
@ -477,6 +472,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved" [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, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n", snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]); 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 * monitor_name manufacture_id product_id
* eld_version edid_version * 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; e->conn_type = val;
else if (!strcmp(name, "port_id")) else if (!strcmp(name, "port_id"))
e->port_id = val; 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]; char name[32];
struct snd_info_entry *entry; struct snd_info_entry *entry;
int err; 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); err = snd_card_proc_new(codec->bus->card, name, &entry);
if (err < 0) if (err < 0)
return err; return err;

View file

@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback) if (is_loopback)
add_input_loopback(codec, node->nid, HDA_INPUT, index); 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); 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) if (err < 0)
return err; return err;
created = 1; created = 1;
@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback) if (is_loopback)
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); 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) if (err < 0)
return err; return err;
created = 1; 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)) { (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); 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); 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) if (err < 0)
return err; return err;
created = 1; 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)) { (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); 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); 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) if (err < 0)
return err; return err;
created = 1; created = 1;
@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec)
} }
/* create input MUX if multiple sources are available */ /* 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) if (err < 0)
return err; return err;
@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec)
HDA_CODEC_VOLUME(name, adc_node->nid, HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index, spec->input_mux.items[i].index,
HDA_INPUT); 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) if (err < 0)
return err; return err;
} }

View file

@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
return 0; 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 #ifdef CONFIG_SND_HDA_RECONFIG
/* /*

View file

@ -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_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS]; static int probe_only[SNDRV_CARDS];
static int single_cmd; static int single_cmd;
static int enable_msi; static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER #ifdef CONFIG_SND_HDA_PATCH_LOADER
static char *patch[SNDRV_CARDS]; static char *patch[SNDRV_CARDS];
#endif #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_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); 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_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif #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 #ifdef CONFIG_SND_HDA_POWER_SAVE
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@ -404,6 +413,7 @@ struct azx {
unsigned short codec_mask; unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */ int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus; struct hda_bus *bus;
unsigned int beep_mode;
/* CORB/RIRB */ /* CORB/RIRB */
struct azx_rb corb; 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) { if (chip->msi) {
snd_printk(KERN_WARNING SFX "No response from codec, " snd_printk(KERN_WARNING SFX "No response from codec, "
"disabling MSI: last cmd=0x%08x\n", "disabling MSI: last cmd=0x%08x\n",
@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
goto again; 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 (chip->probing) {
/* If this critical timeout happens during the codec probing /* If this critical timeout happens during the codec probing
* phase, this is likely an access to a non-existing codec * 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); err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0) if (err < 0)
continue; continue;
codec->beep_mode = chip->beep_mode;
codecs++; 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) static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
{ {
struct azx *chip = container_of(nb, struct azx, reboot_notifier); struct azx *chip = container_of(nb, struct azx, reboot_notifier);
snd_hda_bus_reboot_notify(chip->bus);
azx_stop_chip(chip); azx_stop_chip(chip);
return NOTIFY_OK; 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 = { static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", 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(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 = { static struct snd_pci_quirk msi_black_list[] __devinitdata = {
SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
{} {}
}; };
@ -2316,10 +2328,12 @@ static void __devinit check_msi(struct azx *chip)
{ {
const struct snd_pci_quirk *q; const struct snd_pci_quirk *q;
chip->msi = enable_msi; if (enable_msi >= 0) {
if (chip->msi) chip->msi = !!enable_msi;
return; 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) { if (q) {
printk(KERN_INFO printk(KERN_INFO
"hda_intel: msi for device %04x:%04x set to %d\n", "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; goto out_free;
card->private_data = chip; card->private_data = chip;
#ifdef CONFIG_SND_HDA_INPUT_BEEP
chip->beep_mode = beep_mode[dev];
#endif
/* create codec instances */ /* create codec instances */
err = azx_codec_create(chip, model[dev]); err = azx_codec_create(chip, model[dev]);
if (err < 0) if (err < 0)

View file

@ -23,6 +23,15 @@
#ifndef __SOUND_HDA_LOCAL_H #ifndef __SOUND_HDA_LOCAL_H
#define __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 * for mixer controls
*/ */
@ -33,6 +42,7 @@
/* mono volume with index (index=0,1,...) (channel=1,2) */ /* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
@ -53,6 +63,7 @@
/* mono mute switch with index (index=0,1,...) (channel=1,2) */ /* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.info = snd_hda_mixer_amp_switch_info, \ .info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_amp_switch_get, \ .get = snd_hda_mixer_amp_switch_get, \
.put = snd_hda_mixer_amp_switch_put, \ .put = snd_hda_mixer_amp_switch_put, \
@ -66,6 +77,28 @@
/* stereo mute switch */ /* stereo mute switch */
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO(xname, nid, 3, 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, int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); 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); struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); 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 */ /* lowlevel accessor with caching; use carefully */
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index); 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, int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps); unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); 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); 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; } static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif #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 #ifdef CONFIG_SND_HDA_RECONFIG
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
#else #else
@ -490,7 +544,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
* AMP control callbacks * AMP control callbacks
*/ */
/* retrieve parameters from private_value */ /* 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_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
@ -516,9 +571,11 @@ struct cea_sad {
* ELD: EDID Like Data * ELD: EDID Like Data
*/ */
struct hdmi_eld { struct hdmi_eld {
bool monitor_present;
bool eld_valid;
int eld_size; int eld_size;
int baseline_len; int baseline_len;
int eld_ver; /* (eld_ver == 0) indicates invalid ELD */ int eld_ver;
int cea_edid_ver; int cea_edid_ver;
char monitor_name[ELD_MAX_MNL + 1]; char monitor_name[ELD_MAX_MNL + 1];
int manufacture_id; 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); void snd_hdmi_show_eld(struct hdmi_eld *eld);
#ifdef CONFIG_PROC_FS #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); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
#else #else
static inline int snd_hda_eld_proc_new(struct hda_codec *codec, static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
struct hdmi_eld *eld) struct hdmi_eld *eld,
int index)
{ {
return 0; return 0;
} }

View file

@ -26,6 +26,21 @@
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.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 const char *get_wid_type_name(unsigned int wid_value)
{ {
static char *names[16] = { static char *names[16] = {
@ -46,6 +61,41 @@ static const char *get_wid_type_name(unsigned int wid_value)
return "UNKNOWN Widget"; 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, static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir) 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, static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid) 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, int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 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", snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
get_pwr_state(pwr & AC_PWRST_SETTING), get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >> 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, (data & (1<<i)) ? 1 : 0,
(unsol & (1<<i)) ? 1 : 0); (unsol & (1<<i)) ? 1 : 0);
/* FIXME: add GPO and GPI pin information */ /* FIXME: add GPO and GPI pin information */
print_nid_mixers(buffer, codec, nid);
} }
static void print_codec_info(struct snd_info_entry *entry, 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, " CP");
snd_iprintf(buffer, "\n"); 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 /* volume knob is a special widget that always have connection
* list * list
*/ */

View file

@ -156,15 +156,19 @@ static const char *ad_slave_sws[] = {
static void ad198x_free_kctls(struct hda_codec *codec); 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 */ /* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new ad_beep_mixer[] = { static struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), 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 */ { } /* end */
}; };
#define set_beep_amp(spec, nid, idx, dir) \ #define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ ((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) 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 */ /* create beep controls if needed */
#ifdef CONFIG_SND_HDA_INPUT_BEEP
if (spec->beep_amp) { if (spec->beep_amp) {
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
for (knew = ad_beep_mixer; knew->name; knew++) { for (knew = ad_beep_mixer; knew->name; knew++) {
@ -202,11 +207,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
if (!kctl) if (!kctl)
return -ENOMEM; return -ENOMEM;
kctl->private_value = spec->beep_amp; 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) if (err < 0)
return err; return err;
} }
} }
#endif
/* if we have no master control, let's create it */ /* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 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) static void ad1986a_automic(struct hda_codec *codec)
{ {
unsigned int present; 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 */ /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, 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 #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) static void ad1986a_hp_automute(struct hda_codec *codec)
{ {
struct ad198x_spec *spec = codec->spec; 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 = snd_hda_jack_detect(codec, 0x1a);
spec->jack_present = !!(present & 0x80000000);
if (spec->inv_jack_detect) if (spec->inv_jack_detect)
spec->jack_present = !spec->jack_present; spec->jack_present = !spec->jack_present;
ad1986a_update_hp(codec); ad1986a_update_hp(codec);
@ -1547,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
{ {
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x06, 0, present = snd_hda_jack_detect(codec, 0x06);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 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; unsigned int present;
present = snd_hda_codec_read(codec, 0x08, 0, present = snd_hda_jack_detect(codec, 0x08);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
if (present) if (present)
snd_hda_sequence_write(codec, mic_jack_on); snd_hda_sequence_write(codec, mic_jack_on);
else else
@ -2524,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
{ {
if ((res >> 26) != AD1988_HP_EVENT) if ((res >> 26) != AD1988_HP_EVENT)
return; 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); snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
else else
snd_hda_sequence_write(codec, ad1988_laptop_hp_off); 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); knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name) if (! knew->name)
return -ENOMEM; return -ENOMEM;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val; knew->private_value = val;
return 0; return 0;
} }
@ -3768,8 +3774,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec)
{ {
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x11, 0, present = snd_hda_jack_detect(codec, 0x11);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, 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; unsigned int present;
present = snd_hda_codec_read(codec, 0x14, 0, present = snd_hda_jack_detect(codec, 0x14);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
present ? 0 : 1); present ? 0 : 1);
} }
@ -3817,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec)
{ {
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); present = snd_hda_jack_detect(codec, 0x11);
present &= AC_PINSENSE_PRESENCE; if (!present)
if (!present) { present = snd_hda_jack_detect(codec, 0x12);
present = snd_hda_codec_read(codec, 0x12, 0,
AC_VERB_GET_PIN_SENSE, 0);
present &= AC_PINSENSE_PRESENCE;
}
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, 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; unsigned int idx;
if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & if (snd_hda_jack_detect(codec, 0x14))
AC_PINSENSE_PRESENCE)
idx = 0; idx = 0;
else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & else if (snd_hda_jack_detect(codec, 0x1c))
AC_PINSENSE_PRESENCE)
idx = 4; idx = 4;
else else
idx = 1; idx = 1;
@ -4008,8 +4006,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
{ {
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) present = snd_hda_jack_detect(codec, 0x11);
& AC_PINSENSE_PRESENCE;
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 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 */ /* switch to external mic if plugged */
static void ad1984a_touchsmart_automic(struct hda_codec *codec) static void ad1984a_touchsmart_automic(struct hda_codec *codec)
{ {
if (snd_hda_codec_read(codec, 0x1c, 0, if (snd_hda_jack_detect(codec, 0x1c))
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
snd_hda_codec_write(codec, 0x0c, 0, snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x4); AC_VERB_SET_CONNECT_SEL, 0x4);
} else { else
snd_hda_codec_write(codec, 0x0c, 0, snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x5); AC_VERB_SET_CONNECT_SEL, 0x5);
}
} }

View file

@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
struct snd_kcontrol_new knew = struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); 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, 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 = struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); 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) #define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)

View file

@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
knew.private_value = pval; knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec); *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, 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; knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec); *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) 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 = spec->vmaster_sw =
snd_ctl_make_virtual_master("Master Playback Switch", NULL); 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) if (err < 0)
return err; return err;
snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
spec->vmaster_vol = spec->vmaster_vol =
snd_ctl_make_virtual_master("Master Playback Volume", tlv); 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) if (err < 0)
return err; return err;
return 0; return 0;
@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec)
if (!kctl) if (!kctl)
return -ENOMEM; return -ENOMEM;
kctl->private_value = (long)spec->capture_bind[i]; 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) if (err < 0)
return err; return err;
} }
if (spec->num_inputs > 1 && !spec->mic_detect) { 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)); snd_ctl_new1(&cs_capture_source, codec));
if (err < 0) if (err < 0)
return err; return err;
@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec)
{ {
struct cs_spec *spec = codec->spec; struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int caps, present, hp_present; unsigned int caps, hp_present;
hda_nid_t nid; hda_nid_t nid;
int i; int i;
@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec)
caps = snd_hda_query_pin_caps(codec, nid); caps = snd_hda_query_pin_caps(codec, nid);
if (!(caps & AC_PINCAP_PRES_DETECT)) if (!(caps & AC_PINCAP_PRES_DETECT))
continue; continue;
if (caps & AC_PINCAP_TRIG_REQ) hp_present = snd_hda_jack_detect(codec, nid);
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;
if (hp_present) if (hp_present)
break; break;
} }
@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec)
struct cs_spec *spec = codec->spec; struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid; hda_nid_t nid;
unsigned int caps, present; unsigned int present;
nid = cfg->input_pins[spec->automic_idx]; nid = cfg->input_pins[spec->automic_idx];
caps = snd_hda_query_pin_caps(codec, nid); present = snd_hda_jack_detect(codec, nid);
if (caps & AC_PINCAP_TRIG_REQ) if (present)
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)
change_cur_input(codec, spec->automic_idx, 0); change_cur_input(codec, spec->automic_idx, 0);
else { else {
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?

View file

@ -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++) { for (i = 0; i < spec->jacks.used; i++) {
if (jacks->nid == nid) { if (jacks->nid == nid) {
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, nid, 0, present = snd_hda_jack_detect(codec, nid);
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
present = (present) ? jacks->type : 0 ; present = (present) ? jacks->type : 0 ;
@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec)
}; };
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x12, 0, present = snd_hda_jack_detect(codec, 0x12);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
if (present) if (present)
snd_hda_sequence_write(codec, mic_jack_on); snd_hda_sequence_write(codec, mic_jack_on);
else else
@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
unsigned int bits; unsigned int bits;
spec->hp_present = snd_hda_codec_read(codec, 0x11, 0, spec->hp_present = snd_hda_jack_detect(codec, 0x11);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 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) { switch (codec->subsystem_id >> 16) {
case 0x103c: case 0x103c:
/* HP laptop has a really bad sound over 0dB on NID 0x17. case 0x1734:
* Fix max PCM level to 0 dB /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB
* (originall it has 0x2b steps with 0dB offset 0x14) * 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, snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
(0x14 << AC_AMPCAP_OFFSET_SHIFT) | (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
@ -1243,8 +1240,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
unsigned int bits; unsigned int bits;
spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, spec->hp_present = snd_hda_jack_detect(codec, 0x13);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
/* See the note in cxt5047_hp_master_sw_put */ /* 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; unsigned int present;
present = snd_hda_codec_read(codec, 0x15, 0, present = snd_hda_jack_detect(codec, 0x15);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
if (present) if (present)
snd_hda_sequence_write(codec, mic_jack_on); snd_hda_sequence_write(codec, mic_jack_on);
else else
@ -1415,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
.get = conexant_mux_enum_get, .get = conexant_mux_enum_get,
.put = conexant_mux_enum_put, .put = conexant_mux_enum_put,
}, },
HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
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),
{ } /* end */ { } /* end */
}; };
@ -1621,9 +1607,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec)
if (spec->no_auto_mic) if (spec->no_auto_mic)
return; return;
present = snd_hda_codec_read(codec, 0x17, 0, present = snd_hda_jack_detect(codec, 0x17);
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
snd_hda_codec_write(codec, 0x14, 0, snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_CONNECT_SEL, AC_VERB_SET_CONNECT_SEL,
present ? 0x01 : 0x00); present ? 0x01 : 0x00);
@ -1638,9 +1622,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
if (spec->no_auto_mic) if (spec->no_auto_mic)
return; return;
present = snd_hda_codec_read(codec, 0x18, 0, present = snd_hda_jack_detect(codec, 0x18);
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
if (present) if (present)
spec->cur_adc_idx = 1; spec->cur_adc_idx = 1;
else else
@ -1661,9 +1643,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, spec->hp_present = snd_hda_jack_detect(codec, 0x16);
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
cxt5051_update_speaker(codec); cxt5051_update_speaker(codec);
} }
@ -2011,8 +1991,47 @@ static void cxt5066_automic(struct hda_codec *codec)
}; };
unsigned int present; unsigned int present;
present = snd_hda_codec_read(codec, 0x1a, 0, present = snd_hda_jack_detect(codec, 0x1a);
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 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) { if (present) {
snd_printdd("CXT5066: external microphone detected\n"); snd_printdd("CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present); 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; unsigned int portA, portD;
/* Port A */ /* Port A */
portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) portA = snd_hda_jack_detect(codec, 0x19);
& AC_PINSENSE_PRESENCE;
/* Port D */ /* Port D */
portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) portD = snd_hda_jack_detect(codec, 0x1c);
& AC_PINSENSE_PRESENCE) << 1;
spec->hp_present = !!(portA | portD); spec->hp_present = !!(portA | portD);
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", 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 = { static const struct hda_input_mux cxt5066_analog_mic_boost = {
.num_items = 5, .num_items = 5,
.items = { .items = {
@ -2297,6 +2328,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{ } /* end */ { } /* 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[] = { static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ } /* end */ { } /* end */
@ -2318,6 +2410,7 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
CXT5066_MODELS CXT5066_MODELS
}; };
@ -2325,6 +2418,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop", [CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
[CXT5066_DELL_VOSTO] = "dell-vostro"
}; };
static struct snd_pci_quirk cxt5066_cfg_tbl[] = { 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", SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP), CXT5066_DELL_LAPTOP),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), 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 */ /* no S/PDIF out */
spec->multiout.dig_out_nid = 0; 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 */ /* input source automatically selected */
spec->input_mux = NULL; spec->input_mux = NULL;
break; break;
@ -2417,6 +2525,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5051 }, .patch = patch_cxt5051 },
{ .id = 0x14f15066, .name = "CX20582 (Pebble)", { .id = 0x14f15066, .name = "CX20582 (Pebble)",
.patch = patch_cxt5066 }, .patch = patch_cxt5066 },
{ .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
.patch = patch_cxt5066 },
{} /* terminator */ {} /* 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:14f15047");
MODULE_ALIAS("snd-hda-codec-id:14f15051"); MODULE_ALIAS("snd-hda-codec-id:14f15051");
MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec"); MODULE_DESCRIPTION("Conexant HD-audio codec");

View file

@ -33,15 +33,41 @@
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.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 intel_hdmi_spec {
struct hda_multi_out multiout; int num_cvts;
struct hda_pcm pcm_rec; int num_pins;
struct hdmi_eld sink_eld; 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 { 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 } }, { .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 * HDMI routines
*/ */
#ifdef BE_PARANOID #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 *packet_index, int *byte_index)
{ {
int val; 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; *packet_index = val >> 5;
*byte_index = val & 0x1f; *byte_index = val & 0x1f;
} }
#endif #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 packet_index, int byte_index)
{ {
int val; int val;
val = (packet_index << 5) | (byte_index & 0x1f); 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) 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 */ /* Unmute */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) 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 * 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); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, 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 * 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); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
AC_DIPXMIT_DISABLE); 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); 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))
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
if (chs != hdmi_get_channel_count(codec))
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 #ifdef CONFIG_SND_DEBUG_VERBOSE
int i; int i;
int slot; int slot;
for (i = 0; i < 8; i++) { 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); AC_VERB_GET_HDMI_CHAN_SLOT, i);
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
slot >> 4, slot & 0x7); slot >> 4, slot & 0xf);
} }
#endif #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 * 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 #ifdef CONFIG_SND_DEBUG_VERBOSE
int i; int i;
@ -310,7 +473,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
#endif #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 #ifdef BE_PARANOID
int i, j; int i, j;
@ -339,23 +502,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
#endif #endif
} }
static void hdmi_fill_audio_infoframe(struct hda_codec *codec, static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
struct hdmi_audio_infoframe *ai)
{ {
u8 *params = (u8 *)ai; u8 *bytes = (u8 *)ai;
u8 sum = 0; u8 sum = 0;
int i; int i;
hdmi_debug_dip_size(codec); ai->checksum = 0;
hdmi_clear_dip_buffers(codec); /* be paranoid */
for (i = 0; i < sizeof(*ai); i++)
sum += bytes[i];
for (i = 0; i < sizeof(ai); i++)
sum += params[i];
ai->checksum = - sum; 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); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
for (i = 0; i < sizeof(ai); i++) for (i = 0; i < sizeof(*ai); i++)
hdmi_write_dip_byte(codec, pin_nid, params[i]); 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. * 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 hdmi_audio_infoframe *ai)
{ {
struct intel_hdmi_spec *spec = codec->spec; struct intel_hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->sink_eld; struct hdmi_eld *eld;
int i; int i;
int spk_mask = 0; int spk_mask = 0;
int channels = 1 + (ai->CC02_CT47 & 0x7); int channels = 1 + (ai->CC02_CT47 & 0x7);
@ -402,6 +577,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
if (channels <= 2) if (channels <= 2)
return 0; 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. * HDMI sink's ELD info cannot always be retrieved for now, e.g.
* in console or for audio devices. Assume the highest speakers * in console or for audio devices. Assume the highest speakers
@ -439,8 +619,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
return ai->CA; 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) struct hdmi_audio_infoframe *ai)
{ {
int i; int i;
@ -453,17 +633,41 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
*/ */
for (i = 0; i < 8; i++) 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, AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i); (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 snd_pcm_substream *substream)
{ {
struct intel_hdmi_spec *spec = codec->spec;
hda_nid_t pin_nid;
int i;
struct hdmi_audio_infoframe ai = { struct hdmi_audio_infoframe ai = {
.type = 0x84, .type = 0x84,
.ver = 0x01, .ver = 0x01,
@ -471,11 +675,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
.CC02_CT47 = substream->runtime->channels - 1, .CC02_CT47 = substream->runtime->channels - 1,
}; };
hdmi_setup_channel_allocation(codec, &ai); hdmi_setup_channel_allocation(codec, nid, &ai);
hdmi_setup_channel_mapping(codec, &ai); hdmi_setup_channel_mapping(codec, nid, &ai);
hdmi_fill_audio_infoframe(codec, &ai); for (i = 0; i < spec->num_pins; i++) {
hdmi_start_infoframe_trans(codec); 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) 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 pind = !!(res & AC_UNSOL_RES_PD);
int eldv = !!(res & AC_UNSOL_RES_ELDV); int eldv = !!(res & AC_UNSOL_RES_ELDV);
int index;
printk(KERN_INFO printk(KERN_INFO
"HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
pind, eldv); 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) { 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 */ /* TODO: do real things about ELD */
} }
} }
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) 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 subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO 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, subtag,
cp_state, cp_state,
cp_ready); 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) 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 tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_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); snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
return; return;
} }
@ -538,24 +766,29 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
* Callbacks * Callbacks
*/ */
static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
struct hda_codec *codec, u32 stream_tag, int format)
struct snd_pcm_substream *substream)
{ {
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, snd_printdd("hdmi_setup_stream: "
struct hda_codec *codec, "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
struct snd_pcm_substream *substream) nid,
{ tag == stream_tag ? "" : "new-",
struct intel_hdmi_spec *spec = codec->spec; stream_tag,
fmt == format ? "" : "new-",
format);
hdmi_stop_infoframe_trans(codec); if (tag != stream_tag)
snd_hda_codec_write(codec, nid, 0,
return snd_hda_multi_out_dig_close(codec, &spec->multiout); 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, 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, unsigned int format,
struct snd_pcm_substream *substream) 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, hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
format, substream);
hdmi_set_channel_count(codec, substream->runtime->channels); hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
return 0;
hdmi_setup_audio_infoframe(codec, substream); }
static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
return 0; return 0;
} }
static struct hda_pcm_stream intel_hdmi_pcm_playback = { static struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1, .substreams = 1,
.channels_min = 2, .channels_min = 2,
.channels_max = 8,
.ops = { .ops = {
.open = intel_hdmi_playback_pcm_open, .prepare = intel_hdmi_playback_pcm_prepare,
.close = intel_hdmi_playback_pcm_close, .cleanup = intel_hdmi_playback_pcm_cleanup,
.prepare = intel_hdmi_playback_pcm_prepare
}, },
}; };
static int intel_hdmi_build_pcms(struct hda_codec *codec) static int intel_hdmi_build_pcms(struct hda_codec *codec)
{ {
struct intel_hdmi_spec *spec = codec->spec; 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; codec->pcm_info = info;
/* NID to query formats and rates and setup streams */ for (i = 0; i < codec->num_pcms; i++, info++) {
intel_hdmi_pcm_playback.nid = cvt_nid; unsigned int chans;
info->name = "INTEL HDMI"; chans = get_wcaps(codec, spec->cvt[i]);
info->pcm_type = HDA_PCM_TYPE_HDMI; chans = get_wcaps_channels(chans);
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
info->name = intel_hdmi_pcm_names[i];
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
intel_hdmi_pcm_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
}
return 0; return 0;
} }
@ -609,29 +852,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
{ {
struct intel_hdmi_spec *spec = codec->spec; struct intel_hdmi_spec *spec = codec->spec;
int err; 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++) {
if (err < 0) err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
return err; if (err < 0)
return err;
}
return 0; return 0;
} }
static int intel_hdmi_init(struct hda_codec *codec) 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++) {
AC_VERB_SET_UNSOLICITED_ENABLE, hdmi_enable_output(codec, spec->pin[i]);
AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); snd_hda_codec_write(codec, spec->pin[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | spec->pin[i]);
}
return 0; return 0;
} }
static void intel_hdmi_free(struct hda_codec *codec) static void intel_hdmi_free(struct hda_codec *codec)
{ {
struct intel_hdmi_spec *spec = codec->spec; 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); kfree(spec);
} }
@ -643,49 +896,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
.unsol_event = intel_hdmi_unsol_event, .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; struct intel_hdmi_spec *spec;
int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
return -ENOMEM; return -ENOMEM;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 8;
spec->multiout.dig_out_nid = cvt_nid;
codec->spec = spec; codec->spec = spec;
if (intel_hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = intel_hdmi_patch_ops; 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(); init_channel_allocations();
return 0; 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[] = { static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
{ .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 }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */ {} /* terminator */
}; };

File diff suppressed because it is too large Load diff

View file

@ -93,6 +93,7 @@ enum {
STAC_92HD83XXX_REF, STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF, STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14, STAC_DELL_S14,
STAC_92HD83XXX_HP,
STAC_92HD83XXX_MODELS STAC_92HD83XXX_MODELS
}; };
@ -1085,7 +1086,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (!spec->auto_mic && spec->num_dmuxes > 0 && if (!spec->auto_mic && spec->num_dmuxes > 0 &&
snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
stac_dmux_mixer.count = spec->num_dmuxes; 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)); snd_ctl_new1(&stac_dmux_mixer, codec));
if (err < 0) if (err < 0)
return err; return err;
@ -1101,7 +1102,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
spec->spdif_mute = 1; spec->spdif_mute = 1;
} }
stac_smux_mixer.count = spec->num_smuxes; 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)); snd_ctl_new1(&stac_smux_mixer, codec));
if (err < 0) if (err < 0)
return err; return err;
@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
[STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_REF] = "ref",
[STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref",
[STAC_DELL_S14] = "dell-s14", [STAC_DELL_S14] = "dell-s14",
[STAC_92HD83XXX_HP] = "hp",
}; };
static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { 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), "DFI LanParty", STAC_92HD83XXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
"unknown Dell", STAC_DELL_S14), "unknown Dell", STAC_DELL_S14),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
"HP", STAC_92HD83XXX_HP),
{} /* terminator */ {} /* terminator */
}; };
@ -2648,6 +2652,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
enum { enum {
STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE, STAC_CTL_WIDGET_MUTE,
STAC_CTL_WIDGET_MUTE_BEEP,
STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_MONO_MUX,
STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_IO_SWITCH,
@ -2658,6 +2663,7 @@ enum {
static struct snd_kcontrol_new stac92xx_control_templates[] = { static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0),
HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
STAC_MONO_MUX, STAC_MONO_MUX,
STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_IO_SWITCH(NULL, 0),
@ -2669,7 +2675,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
static struct snd_kcontrol_new * static struct snd_kcontrol_new *
stac_control_new(struct sigmatel_spec *spec, stac_control_new(struct sigmatel_spec *spec,
struct snd_kcontrol_new *ktemp, struct snd_kcontrol_new *ktemp,
const char *name) const char *name,
hda_nid_t nid)
{ {
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
@ -2685,6 +2692,8 @@ stac_control_new(struct sigmatel_spec *spec,
spec->kctls.alloced--; spec->kctls.alloced--;
return NULL; return NULL;
} }
if (nid)
knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
return knew; return knew;
} }
@ -2693,7 +2702,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
int idx, const char *name, int idx, const char *name,
unsigned long val) 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) if (!knew)
return -ENOMEM; return -ENOMEM;
knew->index = idx; 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) if (!spec->num_adcs || imux->num_items <= 1)
return 0; /* no need for input source control */ return 0; /* no need for input source control */
knew = stac_control_new(spec, &stac_input_src_temp, knew = stac_control_new(spec, &stac_input_src_temp,
stac_input_src_temp.name); stac_input_src_temp.name, 0);
if (!knew) if (!knew)
return -ENOMEM; return -ENOMEM;
knew->count = spec->num_adcs; 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; struct sigmatel_spec *spec = codec->spec;
u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); 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 */ /* check for mute support for the the amp */
if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { 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", "Beep Playback Switch",
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
if (err < 0) 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 snd_ctl_elem_value *ucontrol)
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int enabled = !!ucontrol->value.integer.value[0]; return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
if (codec->beep->enabled != enabled) {
codec->beep->enabled = enabled;
return 1;
}
return 0;
} }
static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { 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) 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; 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 /* If we have no real line-out pin and multiple hp-outs, HPs should
* be set up as multi-channel outputs. * be set up as multi-channel outputs.
*/ */
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && if (is_dual_headphones(codec)) {
spec->autocfg.hp_outs > 1) {
/* Copy hp_outs to line_outs, backup line_outs in /* Copy hp_outs to line_outs, backup line_outs in
* speaker_outs so that the following routines can handle * speaker_outs so that the following routines can handle
* HP pins as primary outputs. * HP pins as primary outputs.
@ -4329,6 +4356,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
snd_array_free(&spec->kctls); 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) static void stac92xx_free(struct hda_codec *codec)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
@ -4336,6 +4385,7 @@ static void stac92xx_free(struct hda_codec *codec)
if (! spec) if (! spec)
return; return;
stac92xx_shutup(codec);
stac92xx_free_jacks(codec); stac92xx_free_jacks(codec);
snd_array_free(&spec->events); 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); 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) if (!nid)
return 0; return 0;
if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT
& (1 << 31)) * 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 1;
return 0; return 0;
} }
@ -4791,28 +4845,28 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
return 0; 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 #endif
static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
{ {
struct sigmatel_spec *spec = codec->spec; stac92xx_shutup(codec);
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);
return 0; return 0;
} }
#endif #endif
@ -4827,6 +4881,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
.suspend = stac92xx_suspend, .suspend = stac92xx_suspend,
.resume = stac92xx_resume, .resume = stac92xx_resume,
#endif #endif
.reboot_notify = stac92xx_shutup,
}; };
static int patch_stac9200(struct hda_codec *codec) static int patch_stac9200(struct hda_codec *codec)
@ -5172,6 +5227,22 @@ again:
break; 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); err = stac92xx_parse_auto_config(codec, 0x1d, 0);
if (!err) { if (!err) {
if (spec->board_config < 0) { if (spec->board_config < 0) {
@ -5207,8 +5278,6 @@ again:
snd_hda_codec_write_cache(codec, nid, 0, snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL, num_dacs); AC_VERB_SET_CONNECT_SEL, num_dacs);
codec->patch_ops = stac92xx_patch_ops;
codec->proc_widget_hook = stac92hd_proc_hook; codec->proc_widget_hook = stac92hd_proc_hook;
return 0; return 0;

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.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 # Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o

View file

@ -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 */ 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) static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
{ {
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 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->i2c_mutex);
mutex_init(&ice->open_mutex); mutex_init(&ice->open_mutex);
ice->gpio.set_mask = snd_ice1712_set_gpio_mask; 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.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.set_data = snd_ice1712_set_gpio_data;
ice->gpio.get_data = snd_ice1712_get_gpio_data; ice->gpio.get_data = snd_ice1712_get_gpio_data;

View file

@ -359,7 +359,9 @@ struct snd_ice1712 {
unsigned int saved[2]; /* for ewx_i2c */ unsigned int saved[2]; /* for ewx_i2c */
/* operators */ /* operators */
void (*set_mask)(struct snd_ice1712 *ice, unsigned int data); 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); 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); void (*set_data)(struct snd_ice1712 *ice, unsigned int data);
unsigned int (*get_data)(struct snd_ice1712 *ice); unsigned int (*get_data)(struct snd_ice1712 *ice);
/* misc operators - move to another place? */ /* misc operators - move to another place? */
@ -377,8 +379,11 @@ struct snd_ice1712 {
unsigned int (*get_rate)(struct snd_ice1712 *ice); unsigned int (*get_rate)(struct snd_ice1712 *ice);
void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate); void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
unsigned char (*set_mclk)(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 #ifdef CONFIG_PM
int (*pm_suspend)(struct snd_ice1712 *); int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(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); 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) static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits)
{ {
ice->gpio.set_mask(ice, bits); ice->gpio.set_mask(ice, bits);

View file

@ -53,6 +53,7 @@
#include "phase.h" #include "phase.h"
#include "wtm.h" #include "wtm.h"
#include "se.h" #include "se.h"
#include "quartet.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{"
PHASE_DEVICE_DESC PHASE_DEVICE_DESC
WTM_DEVICE_DESC WTM_DEVICE_DESC
SE_DEVICE_DESC SE_DEVICE_DESC
QTET_DEVICE_DESC
"{VIA,VT1720}," "{VIA,VT1720},"
"{VIA,VT1724}," "{VIA,VT1724},"
"{ICEnsemble,Generic ICE1724}," "{ICEnsemble,Generic ICE1724},"
@ -104,6 +106,8 @@ static int PRO_RATE_LOCKED;
static int PRO_RATE_RESET = 1; static int PRO_RATE_RESET = 1;
static unsigned int PRO_RATE_DEFAULT = 44100; static unsigned int PRO_RATE_DEFAULT = 44100;
static char *ext_clock_names[1] = { "IEC958 In" };
/* /*
* Basic I/O * 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; 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) 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 */ 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) */ /* set the gpio mask (0 = writable) */
static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) 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 */ 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) static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
{ {
outw(data, ICEREG1724(ice, GPIO_DATA)); outw(data, ICEREG1724(ice, GPIO_DATA));
@ -651,16 +675,22 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY; return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
} }
if (!force && is_pro_rate_locked(ice)) { 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); spin_unlock_irqrestore(&ice->reg_lock, flags);
return (rate == ice->cur_rate) ? 0 : -EBUSY; return (rate == ice->cur_rate) ? 0 : -EBUSY;
} }
old_rate = ice->get_rate(ice); if (force || !ice->is_spdif_master(ice)) {
if (force || (old_rate != rate)) /* force means the rate was switched by ucontrol, otherwise
ice->set_rate(ice, rate); * setting clock rate for internal clock mode */
else if (rate == ice->cur_rate) { old_rate = ice->get_rate(ice);
spin_unlock_irqrestore(&ice->reg_lock, flags); if (force || (old_rate != rate))
return 0; ice->set_rate(ice, rate);
else if (rate == ice->cur_rate) {
spin_unlock_irqrestore(&ice->reg_lock, flags);
return 0;
}
} }
ice->cur_rate = rate; ice->cur_rate = rate;
@ -1016,6 +1046,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0; return 0;
} }
@ -1034,6 +1066,8 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN); VT1724_BUFFER_ALIGN);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0; 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_ctl_elem_info *uinfo)
{ {
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int hw_rates_count = ice->hw_rates->count;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1; 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) if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1) if (uinfo->value.enumerated.item >= hw_rates_count)
strcpy(uinfo->value.enumerated.name, "IEC958 Input"); /* ext_clock items */
strcpy(uinfo->value.enumerated.name,
ice->ext_clock_names[
uinfo->value.enumerated.item - hw_rates_count]);
else else
/* int clock items */
sprintf(uinfo->value.enumerated.name, "%d", sprintf(uinfo->value.enumerated.name, "%d",
ice->hw_rates->list[uinfo->value.enumerated.item]); ice->hw_rates->list[uinfo->value.enumerated.item]);
return 0; return 0;
@ -1809,7 +1849,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
spin_lock_irq(&ice->reg_lock); spin_lock_irq(&ice->reg_lock);
if (ice->is_spdif_master(ice)) { 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 { } else {
rate = ice->get_rate(ice); rate = ice->get_rate(ice);
ucontrol->value.enumerated.item[0] = 0; ucontrol->value.enumerated.item[0] = 0;
@ -1824,8 +1865,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
return 0; 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 */ /* 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 oval;
unsigned char i2s_oval; unsigned char i2s_oval;
@ -1834,27 +1881,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
/* setting 256fs */ /* setting 256fs */
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, 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, static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int old_rate, new_rate; unsigned int old_rate, new_rate;
unsigned int item = ucontrol->value.enumerated.item[0]; 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; return -EINVAL;
/* if rate = 0 => external clock */
spin_lock_irq(&ice->reg_lock); spin_lock_irq(&ice->reg_lock);
if (ice->is_spdif_master(ice)) if (ice->is_spdif_master(ice))
old_rate = 0; old_rate = 0;
else else
old_rate = ice->get_rate(ice); old_rate = ice->get_rate(ice);
if (item == spdif) { if (item >= first_ext_clock) {
/* switching to external clock via SPDIF */ /* switching to external clock */
ice->set_spdif_clock(ice); ice->set_spdif_clock(ice, item - first_ext_clock);
new_rate = 0; new_rate = 0;
} else { } else {
/* internal on-card clock */ /* 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); 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) { if (old_rate != new_rate && !new_rate) {
/* notify akm chips as well */ /* notify akm chips as well */
unsigned int i; unsigned int i;
@ -2136,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_phase_cards, snd_vt1724_phase_cards,
snd_vt1724_wtm_cards, snd_vt1724_wtm_cards,
snd_vt1724_se_cards, snd_vt1724_se_cards,
snd_vt1724_qtet_cards,
NULL, NULL,
}; };
@ -2434,7 +2485,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
mutex_init(&ice->open_mutex); mutex_init(&ice->open_mutex);
mutex_init(&ice->i2c_mutex); mutex_init(&ice->i2c_mutex);
ice->gpio.set_mask = snd_vt1724_set_gpio_mask; 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.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.set_data = snd_vt1724_set_gpio_data;
ice->gpio.get_data = snd_vt1724_get_gpio_data; ice->gpio.get_data = snd_vt1724_get_gpio_data;
ice->card = card; ice->card = card;
@ -2522,6 +2575,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return err; return err;
} }
/* field init before calling chip_init */
ice->ext_clock_count = 0;
for (tbl = card_tables; *tbl; tbl++) { for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) { for (c = *tbl; c->subvendor; c++) {
if (c->subvendor == ice->eeprom.subvendor) { if (c->subvendor == ice->eeprom.subvendor) {
@ -2560,6 +2616,13 @@ __found:
ice->set_mclk = stdclock_set_mclk; ice->set_mclk = stdclock_set_mclk;
if (!ice->set_spdif_clock) if (!ice->set_spdif_clock)
ice->set_spdif_clock = stdclock_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) if (!ice->hw_rates)
set_std_hw_rates(ice); 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) { if (ice->pm_saved_is_spdif_master) {
/* switching to external clock via SPDIF */ /* switching to external clock via SPDIF */
ice->set_spdif_clock(ice); ice->set_spdif_clock(ice, 0);
} else { } else {
/* internal on-card clock */ /* internal on-card clock */
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);

View file

@ -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 = { static char *slave_vols[] __devinitdata = {
PCM_VOLUME, PCM_VOLUME,
MONITOR_AN_IN_VOLUME, MONITOR_AN_IN_VOLUME,
@ -496,8 +477,6 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
/* only capture SPDIF over AK4114 */ /* only capture SPDIF over AK4114 */
err = snd_ak4114_build(spec->ak4114, NULL, err = snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
ak4358_proc_init(ice);
if (err < 0) if (err < 0)
return err; return err;
return 0; return 0;
@ -575,13 +554,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
} }
/* setting clock to external - SPDIF */ /* 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; unsigned int old;
old = ice->gpio.get_data(ice); old = ice->gpio.get_data(ice);
/* external clock (= 0), multiply 1x, 48kHz */ /* external clock (= 0), multiply 1x, 48kHz */
ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
GPIO_FREQ_48KHZ); GPIO_FREQ_48KHZ);
return 0;
} }
/* Called when ak4114 detects change in the input SPDIF stream */ /* Called when ak4114 detects change in the input SPDIF stream */

1130
sound/pci/ice1712/quartet.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -1,7 +1,8 @@
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
snd-hifier-objs := hifier.o snd-hifier-objs := hifier.o
snd-oxygen-objs := oxygen.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_OXYGEN_LIB) += snd-oxygen-lib.o
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o obj-$(CONFIG_SND_HIFIER) += snd-hifier.o

83
sound/pci/oxygen/cs2000.h Normal file
View file

@ -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

View file

@ -17,6 +17,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
/*
* CMI8788:
*
* SPI 0 -> AK4396
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <sound/control.h> #include <sound/control.h>
@ -51,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, hifier_ids); MODULE_DEVICE_TABLE(pci, hifier_ids);
struct hifier_data { struct hifier_data {
u8 ak4396_ctl2; u8 ak4396_regs[5];
}; };
static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) 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_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 | OXYGEN_SPI_CLOCK_160 |
(0 << OXYGEN_SPI_CODEC_SHIFT) | (0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI, OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value); 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]); struct hifier_data *data = chip->model_data;
ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
if (value != data->ak4396_regs[reg])
ak4396_write(chip, reg, value);
} }
static void hifier_registers_init(struct oxygen *chip) 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; struct hifier_data *data = chip->model_data;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); 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); 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) static void hifier_init(struct oxygen *chip)
{ {
struct hifier_data *data = chip->model_data; 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); hifier_registers_init(chip);
snd_component_add(chip->card, "AK4396"); 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; struct hifier_data *data = chip->model_data;
u8 value; u8 value;
value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000) if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL; value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000) else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE; value |= AK4396_DFS_DOUBLE;
else else
value |= AK4396_DFS_QUAD; value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */ 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_2, value); ak4396_write(chip, AK4396_CONTROL_1,
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); AK4396_DIF_24_MSB);
ak4396_write(chip, AK4396_CONTROL_2, value);
ak4396_write(chip, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN);
}
}
static void update_ak4396_volume(struct oxygen *chip)
{
ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
} }
static void update_ak4396_mute(struct oxygen *chip) 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; struct hifier_data *data = chip->model_data;
u8 value; u8 value;
value = data->ak4396_ctl2 & ~AK4396_SMUTE; value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute) if (chip->dac_mute)
value |= AK4396_SMUTE; value |= AK4396_SMUTE;
data->ak4396_ctl2 = value; ak4396_write_cached(chip, AK4396_CONTROL_2, value);
ak4396_write(chip, AK4396_CONTROL_2, value);
} }
static void set_cs5340_params(struct oxygen *chip, 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 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 = { static const struct oxygen_model model_hifier = {
.shortname = "C-Media CMI8787", .shortname = "C-Media CMI8787",
.longname = "C-Media Oxygen HD Audio", .longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788", .chip = "CMI8788",
.init = hifier_init, .init = hifier_init,
.control_filter = hifier_control_filter,
.cleanup = hifier_cleanup, .cleanup = hifier_cleanup,
.resume = hifier_resume, .resume = hifier_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params, .set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params, .set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume, .update_dac_volume = update_ak4396_volume,

View file

@ -18,6 +18,8 @@
*/ */
/* /*
* CMI8788:
*
* SPI 0 -> 1st AK4396 (front) * SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround) * SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE) * SPI 2 -> 3rd AK4396 (center/LFE)
@ -27,6 +29,10 @@
* GPIO 0 -> DFS0 of AK5385 * GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385 * GPIO 1 -> DFS1 of AK5385
* GPIO 8 -> enable headphone amplifier on HT-Omega models * 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> #include <linux/delay.h>
@ -91,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_CLARO_HP 0x0100 #define GPIO_CLARO_HP 0x0100
struct generic_data { struct generic_data {
u8 ak4396_ctl2; u8 ak4396_regs[4][5];
u16 saved_wm8785_registers[2]; u16 wm8785_regs[3];
}; };
static void ak4396_write(struct oxygen *chip, unsigned int codec, 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] = { static const u8 codec_spi_map[4] = {
0, 1, 2, 4 0, 1, 2, 4
}; };
struct generic_data *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 | OXYGEN_SPI_CLOCK_160 |
(codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI, OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value); 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) 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) | (3 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO, OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value); (reg << 9) | value);
if (reg < ARRAY_SIZE(data->saved_wm8785_registers)) if (reg < ARRAY_SIZE(data->wm8785_regs))
data->saved_wm8785_registers[reg] = value; data->wm8785_regs[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]);
}
} }
static void ak4396_registers_init(struct oxygen *chip) static void ak4396_registers_init(struct oxygen *chip)
@ -142,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip)
unsigned int i; unsigned int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
ak4396_write(chip, i, ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i, ak4396_write(chip, i, AK4396_CONTROL_2,
AK4396_CONTROL_2, data->ak4396_ctl2); data->ak4396_regs[0][AK4396_CONTROL_2]);
ak4396_write(chip, i, ak4396_write(chip, i, AK4396_CONTROL_3,
AK4396_CONTROL_3, AK4396_PCM); 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) static void ak4396_init(struct oxygen *chip)
{ {
struct generic_data *data = chip->model_data; 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); ak4396_registers_init(chip);
snd_component_add(chip->card, "AK4396"); 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; struct generic_data *data = chip->model_data;
wm8785_write(chip, WM8785_R7, 0); wm8785_write(chip, WM8785_R7, 0);
wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]); wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]); wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
} }
static void wm8785_init(struct oxygen *chip) static void wm8785_init(struct oxygen *chip)
{ {
struct generic_data *data = chip->model_data; struct generic_data *data = chip->model_data;
data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE | data->wm8785_regs[0] =
WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
data->saved_wm8785_registers[1] = WM8785_WL_24; data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
wm8785_registers_init(chip); wm8785_registers_init(chip);
snd_component_add(chip->card, "WM8785"); snd_component_add(chip->card, "WM8785");
} }
@ -264,24 +274,36 @@ static void set_ak4396_params(struct oxygen *chip,
unsigned int i; unsigned int i;
u8 value; 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) if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL; value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000) else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE; value |= AK4396_DFS_DOUBLE;
else else
value |= AK4396_DFS_QUAD; value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */ msleep(1); /* wait for the new MCLK to become stable */
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB);
ak4396_write(chip, i, AK4396_CONTROL_2, value);
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN);
}
}
}
static void update_ak4396_volume(struct oxygen *chip)
{
unsigned int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
ak4396_write(chip, i, ak4396_write_cached(chip, i, AK4396_LCH_ATT,
AK4396_CONTROL_1, AK4396_DIF_24_MSB); chip->dac_volume[i * 2]);
ak4396_write(chip, i, ak4396_write_cached(chip, i, AK4396_RCH_ATT,
AK4396_CONTROL_2, value); chip->dac_volume[i * 2 + 1]);
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
} }
} }
@ -291,21 +313,19 @@ static void update_ak4396_mute(struct oxygen *chip)
unsigned int i; unsigned int i;
u8 value; u8 value;
value = data->ak4396_ctl2 & ~AK4396_SMUTE; value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute) if (chip->dac_mute)
value |= AK4396_SMUTE; value |= AK4396_SMUTE;
data->ak4396_ctl2 = value;
for (i = 0; i < 4; ++i) 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, static void set_wm8785_params(struct oxygen *chip,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
struct generic_data *data = chip->model_data;
unsigned int value; unsigned int value;
wm8785_write(chip, WM8785_R7, 0);
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
if (params_rate(params) <= 48000) if (params_rate(params) <= 48000)
value |= WM8785_OSR_SINGLE; value |= WM8785_OSR_SINGLE;
@ -313,13 +333,11 @@ static void set_wm8785_params(struct oxygen *chip,
value |= WM8785_OSR_DOUBLE; value |= WM8785_OSR_DOUBLE;
else else
value |= WM8785_OSR_QUAD; value |= WM8785_OSR_QUAD;
wm8785_write(chip, WM8785_R0, value); if (value != data->wm8785_regs[0]) {
wm8785_write(chip, WM8785_R7, 0);
if (snd_pcm_format_width(params_format(params)) <= 16) wm8785_write(chip, WM8785_R0, value);
value = WM8785_WL_16; wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
else }
value = WM8785_WL_24;
wm8785_write(chip, WM8785_R1, value);
} }
static void set_ak5385_params(struct oxygen *chip, 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); 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 DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = { static const struct oxygen_model model_generic = {
@ -344,8 +490,10 @@ static const struct oxygen_model model_generic = {
.longname = "C-Media Oxygen HD Audio", .longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788", .chip = "CMI8788",
.init = generic_init, .init = generic_init,
.mixer_init = generic_wm8785_mixer_init,
.cleanup = generic_cleanup, .cleanup = generic_cleanup,
.resume = generic_resume, .resume = generic_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params, .set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params, .set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume, .update_dac_volume = update_ak4396_volume,
@ -374,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
switch (id->driver_data) { switch (id->driver_data) {
case MODEL_MERIDIAN: case MODEL_MERIDIAN:
chip->model.init = meridian_init; chip->model.init = meridian_init;
chip->model.mixer_init = generic_mixer_init;
chip->model.resume = meridian_resume; chip->model.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params; chip->model.set_adc_params = set_ak5385_params;
chip->model.device_config = PLAYBACK_0_TO_I2S | chip->model.device_config = PLAYBACK_0_TO_I2S |
@ -389,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
break; break;
case MODEL_CLARO_HALO: case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init; chip->model.init = claro_halo_init;
chip->model.mixer_init = generic_mixer_init;
chip->model.cleanup = claro_cleanup; chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend; chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume; chip->model.resume = claro_resume;

View file

@ -78,12 +78,15 @@ struct oxygen_model {
void (*resume)(struct oxygen *chip); void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel, void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware); 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, void (*set_dac_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params); struct snd_pcm_hw_params *params);
void (*set_adc_params)(struct oxygen *chip, void (*set_adc_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params); struct snd_pcm_hw_params *params);
void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(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 (*gpio_changed)(struct oxygen *chip);
void (*uart_input)(struct oxygen *chip); void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip, void (*ac97_switch)(struct oxygen *chip,
@ -162,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip);
/* oxygen_pcm.c */ /* oxygen_pcm.c */
int oxygen_pcm_init(struct oxygen *chip); 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 */ /* oxygen_io.c */

View file

@ -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, static void oxygen_restore_eeprom(struct oxygen *chip,
const struct pci_device_id *id) 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 * This function gets called only when a known card model has
* been detected, i.e., we know there is a valid subsystem * 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) static void oxygen_init(struct oxygen *chip)
{ {
unsigned int i; 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); snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free; card->private_free = oxygen_card_free;
pci_bridge_magic();
oxygen_init(chip); oxygen_init(chip);
chip->model.init(chip); chip->model.init(chip);

View file

@ -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 int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{ {
static const char *const names[3] = { static const char *const names[5] = {
"Front", "Front+Surround", "Front+Surround+Back" "Front",
"Front+Surround",
"Front+Surround+Back",
"Front+Surround+Center/LFE",
"Front+Surround+Center/LFE+Back",
}; };
struct oxygen *chip = ctl->private_data; 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->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1; 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) void oxygen_update_dac_routing(struct oxygen *chip)
{ {
/* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */ /* 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 */ /* stereo -> front */
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
(1 << OXYGEN_PLAY_DAC1_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) | (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
(0 << OXYGEN_PLAY_DAC3_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; u8 channels;
unsigned int reg_value; unsigned int reg_value;
@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip)
OXYGEN_PLAY_DAC1_SOURCE_MASK | OXYGEN_PLAY_DAC1_SOURCE_MASK |
OXYGEN_PLAY_DAC2_SOURCE_MASK | OXYGEN_PLAY_DAC2_SOURCE_MASK |
OXYGEN_PLAY_DAC3_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) static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{ {
struct oxygen *chip = ctl->private_data; 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; int changed;
if (value->value.enumerated.item[0] >= count)
return -EINVAL;
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
changed = value->value.enumerated.item[0] != chip->dac_routing; changed = value->value.enumerated.item[0] != chip->dac_routing;
if (changed) { if (changed) {
chip->dac_routing = min(value->value.enumerated.item[0], chip->dac_routing = value->value.enumerated.item[0];
count - 1);
spin_lock_irq(&chip->reg_lock);
oxygen_update_dac_routing(chip); oxygen_update_dac_routing(chip);
spin_unlock_irq(&chip->reg_lock);
} }
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
return changed; return changed;
@ -790,7 +805,7 @@ static const struct {
.controls = { .controls = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Switch", .name = "Analog Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = monitor_get, .get = monitor_get,
.put = monitor_put, .put = monitor_put,
@ -798,7 +813,7 @@ static const struct {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Volume", .name = "Analog Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info, .info = monitor_volume_info,
@ -815,7 +830,7 @@ static const struct {
.controls = { .controls = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Switch", .name = "Analog Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = monitor_get, .get = monitor_get,
.put = monitor_put, .put = monitor_put,
@ -823,7 +838,7 @@ static const struct {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Volume", .name = "Analog Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info, .info = monitor_volume_info,
@ -840,7 +855,7 @@ static const struct {
.controls = { .controls = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Switch", .name = "Analog Input Monitor Playback Switch",
.index = 1, .index = 1,
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = monitor_get, .get = monitor_get,
@ -849,7 +864,7 @@ static const struct {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Monitor Volume", .name = "Analog Input Monitor Playback Volume",
.index = 1, .index = 1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@ -867,7 +882,7 @@ static const struct {
.controls = { .controls = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Input Monitor Switch", .name = "Digital Input Monitor Playback Switch",
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = monitor_get, .get = monitor_get,
.put = monitor_put, .put = monitor_put,
@ -875,7 +890,7 @@ static const struct {
}, },
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Input Monitor Volume", .name = "Digital Input Monitor Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = monitor_volume_info, .info = monitor_volume_info,
@ -954,6 +969,9 @@ static int add_controls(struct oxygen *chip,
if (err == 1) if (err == 1)
continue; continue;
} }
if (!strcmp(template.name, "Stereo Upmixing") &&
chip->model.dac_channels == 2)
continue;
if (!strcmp(template.name, "Master Playback Volume") && if (!strcmp(template.name, "Master Playback Volume") &&
chip->model.dac_tlv) { chip->model.dac_tlv) {
template.tlv.p = chip->model.dac_tlv; template.tlv.p = chip->model.dac_tlv;

View file

@ -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) if (params_rate(hw_params) <= 96000)
return OXYGEN_I2S_MCLK_256; return OXYGEN_I2S_MCLK_256;
else else
return OXYGEN_I2S_MCLK_128; return OXYGEN_I2S_MCLK_128;
} }
EXPORT_SYMBOL(oxygen_default_i2s_mclk);
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) 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_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) | chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
chip->model.adc_i2s_format | chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params), oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97) if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) | chip->model.get_i2s_mclk(chip, PCM_B,
hw_params) |
chip->model.adc_i2s_format | chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params), oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
@ -435,6 +439,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
return err; return err;
mutex_lock(&chip->mutex);
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE); 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_SPDIF_OUT_RATE_MASK);
oxygen_update_spdif_source(chip); oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
mutex_unlock(&chip->mutex);
return 0; return 0;
} }
@ -459,6 +465,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
return err; return err;
mutex_lock(&chip->mutex);
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
oxygen_play_channels(hw_params), 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_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
chip->model.dac_i2s_format | 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_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK | OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK | OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK); OXYGEN_I2S_BITS_MASK);
oxygen_update_dac_routing(chip);
oxygen_update_spdif_source(chip); oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
mutex_lock(&chip->mutex);
chip->model.set_dac_params(chip, hw_params); chip->model.set_dac_params(chip, hw_params);
oxygen_update_dac_routing(chip);
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
return 0; return 0;
} }

File diff suppressed because it is too large Load diff

50
sound/pci/oxygen/xonar.h Normal file
View file

@ -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

View file

@ -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;
}

View file

@ -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, &param);
param = 1;
hdmi_write_command(chip, 0x74, 1, &param);
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, &param);
}
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;
}
}

View file

@ -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