2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-16 17:23:55 +08:00

Merge branch 'topic/hda' into for-linus

* topic/hda: (51 commits)
  ALSA: hda - Fix the previous tagra-8ch patch
  ALSA: hda - Add 7.1 support for MSI GX620
  ALSA: support Sony Vaio TT
  ALSA: hda_intel: fix build error when !PM
  ALSA: hda - More Aspire 8930G fixes
  ALSA: hda - Acer Aspire 8930G support
  ALSA: hda - Limit codec-verb retry to limited hardwares
  ALSA: hda - Add codec bus reset and verb-retry at critical errors
  ALSA: hda - Reorder and clean-up ALC268 quirk table
  ALSA: hda - fix audio on LG R510
  ALSA: hda - Macbook[Pro] 5 6ch support
  ALSA: hda-intel: improve initialization for ALC262_HP_BPC model
  ALSA: hda - Jack Mode changes for Sigmatel boards
  ALSA: hda - Support NVIDIA 8 channel HDMI audio
  ALSA: hda - Fix a typo in the previous patch
  ALSA: hda - Fix reverted LED setup for HP
  ALSA: hda - Add more register bits definitions
  ALSA: hda - Always sync writes in single_cmd mode
  ALSA: hda - Support sync after writing a verb
  ALSA: hda - Allow concurrent RIRB access in single_cmd mode
  ...
This commit is contained in:
Takashi Iwai 2009-06-10 07:26:31 +02:00
commit 81ad969dbf
15 changed files with 2797 additions and 1336 deletions

View File

@ -36,6 +36,7 @@ ALC260
acer Acer TravelMate
will Will laptops (PB V7900)
replacer Replacer 672V
favorit100 Maxdata Favorit 100XS
basic fixed pin assignment (old default model)
test for testing/debugging purpose, almost all controls can
adjusted. Appearing only when compiled with
@ -85,10 +86,11 @@ ALC269
eeepc-p703 ASUS Eeepc P703 P900A
eeepc-p901 ASUS Eeepc P901 S101
fujitsu FSC Amilo
lifebook Fujitsu Lifebook S6420
auto auto-config reading BIOS (default)
ALC662/663
==========
ALC662/663/272
==============
3stack-dig 3-stack (2-channel) with SPDIF
3stack-6ch 3-stack (6-channel)
3stack-6ch-dig 3-stack (6-channel) with SPDIF
@ -107,6 +109,9 @@ ALC662/663
asus-mode4 ASUS
asus-mode5 ASUS
asus-mode6 ASUS
dell Dell with ALC272
dell-zm1 Dell ZM1 with ALC272
samsung-nc10 Samsung NC10 mini notebook
auto auto-config reading BIOS (default)
ALC882/885
@ -118,6 +123,7 @@ ALC882/885
asus-a7j ASUS A7J
asus-a7m ASUS A7M
macpro MacPro support
mb5 Macbook 5,1
mbp3 Macbook Pro rev3
imac24 iMac 24'' with jack detection
w2jc ASUS W2JC
@ -133,10 +139,12 @@ ALC883/888
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
acer-aspire Acer Aspire 9810
acer-aspire-4930g Acer Aspire 4930G
acer-aspire-8930g Acer Aspire 8930G
medion Medion Laptops
medion-md2 Medion MD2
targa-dig Targa/MSI
targa-2ch-dig Targs/MSI with 2-channel
targa-2ch-dig Targa/MSI with 2-channel
targa-8ch-dig Targa/MSI with 8-channel (MSI GX620)
laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
lenovo-101e Lenovo 101E
lenovo-nb0763 Lenovo NB0763
@ -150,6 +158,9 @@ ALC883/888
fujitsu-pi2515 Fujitsu AMILO Pi2515
fujitsu-xa3530 Fujitsu AMILO XA3530
3stack-6ch-intel Intel DG33* boards
asus-p5q ASUS P5Q-EM boards
mb31 MacBook 3,1
sony-vaio-tt Sony VAIO TT
auto auto-config reading BIOS (default)
ALC861/660
@ -348,6 +359,7 @@ STAC92HD71B*
hp-m4 HP mini 1000
hp-dv5 HP dv series
hp-hdx HP HDX series
hp-dv4-1222nr HP dv4-1222nr (with LED support)
auto BIOS setup (default)
STAC92HD73*

View File

@ -139,6 +139,19 @@ config SND_HDA_CODEC_CONEXANT
snd-hda-codec-conexant.
This module is automatically loaded at probing.
config SND_HDA_CODEC_CA0110
bool "Build Creative CA0110-IBG codec support"
depends on SND_HDA_INTEL
default y
help
Say Y here to include Creative CA0110-IBG codec support in
snd-hda-intel driver, found on some Creative X-Fi cards.
When the HD-audio driver is built as a module, the codec
support code is also built as another module,
snd-hda-codec-ca0110.
This module is automatically loaded at probing.
config SND_HDA_CODEC_CMEDIA
bool "Build C-Media HD-audio codec support"
default y

View File

@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-atihdmi-objs := patch_atihdmi.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
@ -40,6 +41,9 @@ endif
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
endif
ifdef CONFIG_SND_HDA_CODEC_CA0110
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
endif
ifdef CONFIG_SND_HDA_CODEC_CONEXANT
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
endif

View File

@ -45,6 +45,46 @@ static void snd_hda_generate_beep(struct work_struct *work)
AC_VERB_SET_BEEP_CONTROL, beep->tone);
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
*
* The tone frequency of beep generator on IDT/STAC codecs is
* defined from the 8bit tone parameter, in Hz,
* freq = 48000 * (257 - tone) / 1024
* that is from 12kHz to 93.75kHz in step of 46.875 hz
*/
static int beep_linear_tone(struct hda_beep *beep, int hz)
{
hz *= 1000; /* fixed point */
hz = hz - DIGBEEP_HZ_MIN;
if (hz < 0)
hz = 0; /* turn off PC beep*/
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
hz = 0xff;
else {
hz /= DIGBEEP_HZ_STEP;
hz++;
}
return hz;
}
/* HD-audio standard beep tone parameter calculation
*
* The tone frequency in Hz is calculated as
* freq = 48000 / (tone * 4)
* from 47Hz to 12kHz
*/
static int beep_standard_tone(struct hda_beep *beep, int hz)
{
if (hz <= 0)
return 0; /* disabled */
hz = 12000 / hz;
if (hz > 0xff)
return 0xff;
if (hz <= 0)
return 1;
return hz;
}
static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
@ -55,21 +95,14 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
if (hz)
hz = 1000;
case SND_TONE:
hz *= 1000; /* fixed point */
hz = hz - DIGBEEP_HZ_MIN;
if (hz < 0)
hz = 0; /* turn off PC beep*/
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
hz = 0xff;
else {
hz /= DIGBEEP_HZ_STEP;
hz++;
}
if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz);
else
beep->tone = beep_standard_tone(beep, hz);
break;
default:
return -1;
}
beep->tone = hz;
/* schedule beep event */
schedule_work(&beep->beep_work);

View File

@ -30,8 +30,9 @@ struct hda_beep {
struct hda_codec *codec;
char phys[32];
int tone;
int nid;
int enabled;
hda_nid_t nid;
unsigned int enabled:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
struct work_struct beep_work; /* scheduled task for beep event */
};

View File

@ -48,6 +48,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" },
{ 0x1102, "Creative" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
{ 0x11c1, "LSI" },
@ -157,6 +158,39 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
return val;
}
/*
* Send and receive a verb
*/
static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
unsigned int *res)
{
struct hda_bus *bus = codec->bus;
int err;
if (res)
*res = -1;
again:
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
err = bus->ops.command(bus, cmd);
if (!err && res)
*res = bus->ops.get_response(bus);
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
if (res && *res == -1 && bus->rirb_error) {
if (bus->response_reset) {
snd_printd("hda_codec: resetting BUS due to "
"fatal communication error\n");
bus->ops.bus_reset(bus);
}
goto again;
}
/* clear reset-flag when the communication gets recovered */
if (!err)
bus->response_reset = 0;
return err;
}
/**
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
@ -173,18 +207,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int direct,
unsigned int verb, unsigned int parm)
{
struct hda_bus *bus = codec->bus;
unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
unsigned int res;
res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
if (!bus->ops.command(bus, res))
res = bus->ops.get_response(bus);
else
res = (unsigned int)-1;
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
codec_exec_verb(codec, cmd, &res);
return res;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_read);
@ -204,17 +229,10 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int parm)
{
struct hda_bus *bus = codec->bus;
unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
unsigned int res;
int err;
res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
err = bus->ops.command(bus, res);
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
return codec_exec_verb(codec, cmd,
codec->bus->sync_write ? &res : NULL);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write);
@ -613,7 +631,10 @@ static int get_codec_name(struct hda_codec *codec)
const struct hda_vendor_id *c;
const char *vendor = NULL;
u16 vendor_id = codec->vendor_id >> 16;
char tmp[16], name[32];
char tmp[16];
if (codec->vendor_name)
goto get_chip_name;
for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) {
@ -625,14 +646,21 @@ static int get_codec_name(struct hda_codec *codec)
sprintf(tmp, "Generic %04x", vendor_id);
vendor = tmp;
}
codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
if (!codec->vendor_name)
return -ENOMEM;
get_chip_name:
if (codec->chip_name)
return 0;
if (codec->preset && codec->preset->name)
snprintf(name, sizeof(name), "%s %s", vendor,
codec->preset->name);
else
snprintf(name, sizeof(name), "%s ID %x", vendor,
codec->vendor_id & 0xffff);
codec->name = kstrdup(name, GFP_KERNEL);
if (!codec->name)
codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
else {
sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
codec->chip_name = kstrdup(tmp, GFP_KERNEL);
}
if (!codec->chip_name)
return -ENOMEM;
return 0;
}
@ -838,7 +866,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
module_put(codec->owner);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
kfree(codec->name);
kfree(codec->vendor_name);
kfree(codec->chip_name);
kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
@ -979,15 +1008,16 @@ int snd_hda_codec_configure(struct hda_codec *codec)
int err;
codec->preset = find_codec_preset(codec);
if (!codec->name) {
if (!codec->vendor_name || !codec->chip_name) {
err = get_codec_name(codec);
if (err < 0)
return err;
}
/* audio codec should override the mixer name */
if (codec->afg || !*codec->bus->card->mixername)
strlcpy(codec->bus->card->mixername, codec->name,
sizeof(codec->bus->card->mixername));
snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
if (is_generic_config(codec)) {
err = snd_hda_parse_generic_codec(codec);
@ -1055,6 +1085,8 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
/* FIXME: more better hash key? */
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
#define INFO_AMP_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
@ -1145,19 +1177,32 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
}
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
static unsigned int
query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
unsigned int (*func)(struct hda_codec *, hda_nid_t))
{
struct hda_amp_info *info;
info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
info = get_alloc_amp_hash(codec, key);
if (!info)
return 0;
if (!info->head.val) {
info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
info->head.val |= INFO_AMP_CAPS;
info->amp_caps = func(codec, nid);
}
return info->amp_caps;
}
static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
read_pin_cap);
}
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
/*
@ -1432,6 +1477,8 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
strcpy(id.name, name);
return snd_ctl_find_id(codec->bus->card, &id);
}
@ -2242,28 +2289,22 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
struct hda_bus *bus = codec->bus;
unsigned int res;
int err;
int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
struct hda_cache_head *c;
u32 key;
res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
err = bus->ops.command(bus, res);
if (!err) {
struct hda_cache_head *c;
u32 key;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
c = get_alloc_hash(&codec->cmd_cache, key);
if (c)
c->val = parm;
}
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
if (err < 0)
return err;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key);
if (c)
c->val = parm;
mutex_unlock(&codec->bus->cmd_mutex);
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
@ -2321,7 +2362,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
if (wcaps & AC_WCAP_POWER) {
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
AC_WCAP_TYPE_SHIFT;
if (wid_type == AC_WID_PIN) {
if (power_state == AC_PWRST_D3 &&
wid_type == AC_WID_PIN) {
unsigned int pincap;
/*
* don't power down the widget if it controls
@ -2333,7 +2375,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
nid, 0,
AC_VERB_GET_EAPD_BTLENABLE, 0);
eapd &= 0x02;
if (power_state == AC_PWRST_D3 && eapd)
if (eapd)
continue;
}
}
@ -2544,6 +2586,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
}
EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int val = 0;
if (nid != codec->afg &&
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (!val || val == -1)
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
if (!val || val == -1)
return 0;
return val;
}
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
get_pcm_param);
}
static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (!streams || streams == -1)
streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (!streams || streams == -1)
return 0;
return streams;
}
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
get_stream_param);
}
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec
@ -2562,15 +2639,8 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
{
unsigned int i, val, wcaps;
val = 0;
wcaps = get_wcaps(codec, nid);
if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return -EIO;
}
if (!val)
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
val = query_pcm_param(codec, nid);
if (ratesp) {
u32 rates = 0;
@ -2592,15 +2662,9 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u64 formats = 0;
unsigned int streams, bps;
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (streams == -1)
streams = query_stream_param(codec, nid);
if (!streams)
return -EIO;
if (!streams) {
streams = snd_hda_param_read(codec, codec->afg,
AC_PAR_STREAM);
if (streams == -1)
return -EIO;
}
bps = 0;
if (streams & AC_SUPFMT_PCM) {
@ -2674,17 +2738,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
int i;
unsigned int val = 0, rate, stream;
if (nid != codec->afg &&
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return 0;
}
if (!val) {
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
if (val == -1)
return 0;
}
val = query_pcm_param(codec, nid);
if (!val)
return 0;
rate = format & 0xff00;
for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
@ -2696,12 +2752,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
if (i >= AC_PAR_PCM_RATE_BITS)
return 0;
stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (stream == -1)
return 0;
if (!stream && nid != codec->afg)
stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (!stream || stream == -1)
stream = query_stream_param(codec, nid);
if (!stream)
return 0;
if (stream & AC_SUPFMT_PCM) {
@ -3835,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
/**
* snd_hda_suspend - suspend the codecs
* @bus: the HDA bus
* @state: suspsend state
*
* Returns 0 if successful.
*/
int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
int snd_hda_suspend(struct hda_bus *bus)
{
struct hda_codec *codec;

View File

@ -574,6 +574,8 @@ struct hda_bus_ops {
/* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus);
@ -622,7 +624,13 @@ struct hda_bus {
/* misc op flags */
unsigned int needs_damn_long_delay :1;
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
unsigned int sync_write:1; /* sync after verb write */
/* status for codec/controller */
unsigned int shutdown :1; /* being unloaded */
unsigned int rirb_error:1; /* error in codec communication */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
};
/*
@ -747,7 +755,8 @@ struct hda_codec {
/* detected preset */
const struct hda_codec_preset *preset;
struct module *owner;
const char *name; /* codec name */
const char *vendor_name; /* codec vendor name */
const char *chip_name; /* codec chip name */
const char *modelname; /* model name for preset */
/* set by patch */
@ -905,7 +914,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
* power management
*/
#ifdef CONFIG_PM
int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif

View File

@ -242,7 +242,8 @@ CODEC_INFO_SHOW(subsystem_id);
CODEC_INFO_SHOW(revision_id);
CODEC_INFO_SHOW(afg);
CODEC_INFO_SHOW(mfg);
CODEC_INFO_STR_SHOW(name);
CODEC_INFO_STR_SHOW(vendor_name);
CODEC_INFO_STR_SHOW(chip_name);
CODEC_INFO_STR_SHOW(modelname);
#define CODEC_INFO_STORE(type) \
@ -275,7 +276,8 @@ static ssize_t type##_store(struct device *dev, \
CODEC_INFO_STORE(vendor_id);
CODEC_INFO_STORE(subsystem_id);
CODEC_INFO_STORE(revision_id);
CODEC_INFO_STR_STORE(name);
CODEC_INFO_STR_STORE(vendor_name);
CODEC_INFO_STR_STORE(chip_name);
CODEC_INFO_STR_STORE(modelname);
#define CODEC_ACTION_STORE(type) \
@ -499,7 +501,8 @@ static struct device_attribute codec_attrs[] = {
CODEC_ATTR_RW(revision_id),
CODEC_ATTR_RO(afg),
CODEC_ATTR_RO(mfg),
CODEC_ATTR_RW(name),
CODEC_ATTR_RW(vendor_name),
CODEC_ATTR_RW(chip_name),
CODEC_ATTR_RW(modelname),
CODEC_ATTR_RW(init_verbs),
CODEC_ATTR_RW(hints),

View File

@ -128,21 +128,33 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{ULI, M5461}}");
MODULE_DESCRIPTION("Intel HDA driver");
#ifdef CONFIG_SND_VERBOSE_PRINTK
#define SFX /* nop */
#else
#define SFX "hda-intel: "
#endif
/*
* registers
*/
#define ICH6_REG_GCAP 0x00
#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
#define ICH6_REG_VMIN 0x02
#define ICH6_REG_VMAJ 0x03
#define ICH6_REG_OUTPAY 0x04
#define ICH6_REG_INPAY 0x06
#define ICH6_REG_GCTL 0x08
#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define ICH6_REG_WAKEEN 0x0c
#define ICH6_REG_STATESTS 0x0e
#define ICH6_REG_GSTS 0x10
#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
#define ICH6_REG_INTCTL 0x20
#define ICH6_REG_INTSTS 0x24
#define ICH6_REG_WALCLK 0x30
@ -150,17 +162,27 @@ MODULE_DESCRIPTION("Intel HDA driver");
#define ICH6_REG_CORBLBASE 0x40
#define ICH6_REG_CORBUBASE 0x44
#define ICH6_REG_CORBWP 0x48
#define ICH6_REG_CORBRP 0x4A
#define ICH6_REG_CORBRP 0x4a
#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
#define ICH6_REG_CORBCTL 0x4c
#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define ICH6_REG_CORBSTS 0x4d
#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define ICH6_REG_CORBSIZE 0x4e
#define ICH6_REG_RIRBLBASE 0x50
#define ICH6_REG_RIRBUBASE 0x54
#define ICH6_REG_RIRBWP 0x58
#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
#define ICH6_REG_RINTCNT 0x5a
#define ICH6_REG_RIRBCTL 0x5c
#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define ICH6_REG_RIRBSTS 0x5d
#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define ICH6_REG_RIRBSIZE 0x5e
#define ICH6_REG_IC 0x60
@ -257,16 +279,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* GCTL unsolicited response enable bit */
#define ICH6_GCTL_UREN (1<<8)
/* GCTL reset bit */
#define ICH6_GCTL_RESET (1<<0)
/* CORB/RIRB control, read/write pointer */
#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */
#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */
#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */
/* below are so far hardcoded - should read registers in future */
#define ICH6_MAX_CORB_ENTRIES 256
#define ICH6_MAX_RIRB_ENTRIES 256
@ -512,25 +524,25 @@ static void azx_init_cmd_io(struct azx *chip)
/* set the corb write pointer to 0 */
azx_writew(chip, CORBWP, 0);
/* reset the corb hw read pointer */
azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
/* enable corb dma */
azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
/* RIRB set up */
chip->rirb.addr = chip->rb.addr + 2048;
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
/* set the rirb size to 256 entries (ULI requires explicitly) */
azx_writeb(chip, RIRBSIZE, 0x02);
/* reset the rirb hw write pointer */
azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
/* set N=1, get RIRB response interrupt for new entry */
azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
chip->rirb.rp = chip->rirb.cmds = 0;
}
static void azx_free_cmd_io(struct azx *chip)
@ -606,6 +618,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
}
if (!chip->rirb.cmds) {
smp_rmb();
bus->rirb_error = 0;
return chip->rirb.res; /* the last value */
}
if (time_after(jiffies, timeout))
@ -619,19 +632,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
}
if (chip->msi) {
snd_printk(KERN_WARNING "hda_intel: No response from codec, "
snd_printk(KERN_WARNING SFX "No response from codec, "
"disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
free_irq(chip->irq, chip);
chip->irq = -1;
pci_disable_msi(chip->pci);
chip->msi = 0;
if (azx_acquire_irq(chip, 1) < 0)
if (azx_acquire_irq(chip, 1) < 0) {
bus->rirb_error = 1;
return -1;
}
goto again;
}
if (!chip->polling_mode) {
snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
"switching to polling mode: last cmd=0x%08x\n",
chip->last_cmd);
chip->polling_mode = 1;
@ -646,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
return -1;
}
/* a fatal communication error; need either to reset or to fallback
* to the single_cmd mode
*/
bus->rirb_error = 1;
if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
bus->response_reset = 1;
return -1; /* give a chance to retry */
}
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd);
chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0;
/* switch to single_cmd mode */
chip->single_cmd = 1;
bus->response_reset = 0;
/* re-initialize CORB/RIRB */
azx_free_cmd_io(chip);
azx_init_cmd_io(chip);
return -1;
}
@ -667,12 +691,34 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
* I left the codes, however, for debugging/testing purposes.
*/
/* receive a response */
static int azx_single_wait_for_response(struct azx *chip)
{
int timeout = 50;
while (timeout--) {
/* check IRV busy bit */
if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
/* reuse rirb.res as the response return value */
chip->rirb.res = azx_readl(chip, IR);
return 0;
}
udelay(1);
}
if (printk_ratelimit())
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
azx_readw(chip, IRS));
chip->rirb.res = -1;
return -EIO;
}
/* send a command */
static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
{
struct azx *chip = bus->private_data;
int timeout = 50;
bus->rirb_error = 0;
while (timeout--) {
/* check ICB busy bit */
if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
@ -682,7 +728,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
azx_writel(chip, IC, val);
azx_writew(chip, IRS, azx_readw(chip, IRS) |
ICH6_IRS_BUSY);
return 0;
return azx_single_wait_for_response(chip);
}
udelay(1);
}
@ -696,18 +742,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
static unsigned int azx_single_get_response(struct hda_bus *bus)
{
struct azx *chip = bus->private_data;
int timeout = 50;
while (timeout--) {
/* check IRV busy bit */
if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
return azx_readl(chip, IR);
udelay(1);
}
if (printk_ratelimit())
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
azx_readw(chip, IRS));
return (unsigned int)-1;
return chip->rirb.res;
}
/*
@ -775,17 +810,17 @@ static int azx_reset(struct azx *chip)
/* check to see if controller is ready */
if (!azx_readb(chip, GCTL)) {
snd_printd("azx_reset: controller not ready!\n");
snd_printd(SFX "azx_reset: controller not ready!\n");
return -EBUSY;
}
/* Accept unsolicited responses */
azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
/* detect codecs */
if (!chip->codec_mask) {
chip->codec_mask = azx_readw(chip, STATESTS);
snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
}
return 0;
@ -895,8 +930,7 @@ static void azx_init_chip(struct azx *chip)
azx_int_enable(chip);
/* initialize the codec command I/O */
if (!chip->single_cmd)
azx_init_cmd_io(chip);
azx_init_cmd_io(chip);
/* program the position buffer */
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@ -953,12 +987,12 @@ static void azx_init_pci(struct azx *chip)
case AZX_DRIVER_SCH:
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \
pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
pci_read_config_word(chip->pci,
INTEL_SCH_HDA_DEVC, &snoop);
snd_printdd("HDA snoop disabled, enabling ... %s\n",\
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \
snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
? "Failed" : "OK");
}
break;
@ -1012,7 +1046,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
if (!chip->single_cmd && (status & RIRB_INT_RESPONSE))
if (status & RIRB_INT_RESPONSE)
azx_update_rirb(chip);
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
@ -1098,7 +1132,7 @@ static int azx_setup_periods(struct azx *chip,
pos_align;
pos_adj = frames_to_bytes(runtime, pos_adj);
if (pos_adj >= period_bytes) {
snd_printk(KERN_WARNING "Too big adjustment %d\n",
snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
bdl_pos_adj[chip->dev_index]);
pos_adj = 0;
} else {
@ -1122,7 +1156,7 @@ static int azx_setup_periods(struct azx *chip,
return 0;
error:
snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
azx_dev->bufsize, period_bytes);
return -EINVAL;
}
@ -1215,7 +1249,7 @@ static int probe_codec(struct azx *chip, int addr)
chip->probing = 0;
if (res == -1)
return -EIO;
snd_printdd("hda_intel: codec #%d probed OK\n", addr);
snd_printdd(SFX "codec #%d probed OK\n", addr);
return 0;
}
@ -1223,6 +1257,26 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *cpcm);
static void azx_stop_chip(struct azx *chip);
static void azx_bus_reset(struct hda_bus *bus)
{
struct azx *chip = bus->private_data;
bus->in_reset = 1;
azx_stop_chip(chip);
azx_init_chip(chip);
#ifdef CONFIG_PM
if (chip->initialized) {
int i;
for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]);
snd_hda_suspend(chip->bus);
snd_hda_resume(chip->bus);
}
#endif
bus->in_reset = 0;
}
/*
* Codec initialization
*/
@ -1246,6 +1300,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
bus_temp.ops.bus_reset = azx_bus_reset;
#ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify;
@ -1270,8 +1325,8 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
/* Some BIOSen give you wrong codec addresses
* that don't exist
*/
snd_printk(KERN_WARNING
"hda_intel: Codec #%d probe error; "
snd_printk(KERN_WARNING SFX
"Codec #%d probe error; "
"disabling it...\n", c);
chip->codec_mask &= ~(1 << c);
/* More badly, accessing to a non-existing
@ -1487,7 +1542,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
bufsize = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_bytes(substream);
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
bufsize, format_val);
if (bufsize != azx_dev->bufsize ||
@ -1830,7 +1885,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
&pcm);
if (err < 0)
return err;
strcpy(pcm->name, cpcm->name);
strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
if (apcm == NULL)
return -ENOMEM;
@ -1973,7 +2028,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized)
snd_hda_suspend(chip->bus, state);
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
@ -2265,14 +2320,14 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
synchronize_irq(chip->irq);
gcap = azx_readw(chip, GCAP);
snd_printdd("chipset global capabilities = 0x%x\n", gcap);
snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
/* ATI chips seems buggy about 64bit DMA addresses */
if (chip->driver_type == AZX_DRIVER_ATI)
gcap &= ~0x01;
gcap &= ~ICH6_GCAP_64OK;
/* allow 64bit DMA address if supported by H/W */
if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
else {
pci_set_dma_mask(pci, DMA_BIT_MASK(32));
@ -2309,7 +2364,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
GFP_KERNEL);
if (!chip->azx_dev) {
snd_printk(KERN_ERR "cannot malloc azx_dev\n");
snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
goto errout;
}
@ -2332,11 +2387,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
goto errout;
}
/* allocate CORB/RIRB */
if (!chip->single_cmd) {
err = azx_alloc_cmd_io(chip);
if (err < 0)
goto errout;
}
err = azx_alloc_cmd_io(chip);
if (err < 0)
goto errout;
/* initialize streams */
azx_init_stream(chip);
@ -2359,9 +2412,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
}
strcpy(card->driver, "HDA-Intel");
strcpy(card->shortname, driver_short_names[chip->driver_type]);
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->addr, chip->irq);
strlcpy(card->shortname, driver_short_names[chip->driver_type],
sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, chip->addr, chip->irq);
*rchip = chip;
return 0;
@ -2514,6 +2569,20 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
/* Creative X-Fi (CA0110-IBG) */
#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
/* the following entry conflicts with snd-ctxfi driver,
* as ctxfi driver mutates from HD-audio to native mode with
* a special command sequence.
*/
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
#endif
/* AMD Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,

View File

@ -466,8 +466,12 @@ static void print_codec_info(struct snd_info_entry *entry,
hda_nid_t nid;
int i, nodes;
snd_iprintf(buffer, "Codec: %s\n",
codec->name ? codec->name : "Not Set");
snd_iprintf(buffer, "Codec: ");
if (codec->vendor_name && codec->chip_name)
snd_iprintf(buffer, "%s %s\n",
codec->vendor_name, codec->chip_name);
else
snd_iprintf(buffer, "Not Set\n");
snd_iprintf(buffer, "Address: %d\n", codec->addr);
snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);

View File

@ -0,0 +1,573 @@
/*
* HD audio interface patch for Creative X-Fi CA0110-IBG chip
*
* Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/*
*/
struct ca0110_spec {
struct auto_pin_cfg autocfg;
struct hda_multi_out multiout;
hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
hda_nid_t hp_dac;
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t adcs[AUTO_PIN_LAST];
hda_nid_t dig_out;
hda_nid_t dig_in;
unsigned int num_inputs;
const char *input_labels[AUTO_PIN_LAST];
struct hda_pcm pcm_rec[2]; /* PCM information */
};
/*
* PCM callbacks
*/
static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo);
}
static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
stream_tag, format, substream);
}
static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
/*
* Digital out
*/
static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
format, substream);
}
/*
* Analog capture
*/
static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
stream_tag, 0, format);
return 0;
}
static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0110_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
return 0;
}
/*
*/
static char *dirstr[2] = { "Playback", "Capture" };
static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
int chan, int dir)
{
char namestr[44];
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
}
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
int chan, int dir)
{
char namestr[44];
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
}
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
#define add_mono_switch(codec, nid, pfx, chan) \
_add_switch(codec, nid, pfx, chan, 0)
#define add_mono_volume(codec, nid, pfx, chan) \
_add_volume(codec, nid, pfx, chan, 0)
static int ca0110_build_controls(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
static char *prefix[AUTO_CFG_MAX_OUTS] = {
"Front", "Surround", NULL, "Side", "Multi"
};
hda_nid_t mutenid;
int i, err;
for (i = 0; i < spec->multiout.num_dacs; i++) {
if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
mutenid = spec->out_pins[i];
else
mutenid = spec->multiout.dac_nids[i];
if (!prefix[i]) {
err = add_mono_switch(codec, mutenid,
"Center", 1);
if (err < 0)
return err;
err = add_mono_switch(codec, mutenid,
"LFE", 1);
if (err < 0)
return err;
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
"Center", 1);
if (err < 0)
return err;
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
"LFE", 1);
if (err < 0)
return err;
} else {
err = add_out_switch(codec, mutenid,
prefix[i]);
if (err < 0)
return err;
err = add_out_volume(codec, spec->multiout.dac_nids[i],
prefix[i]);
if (err < 0)
return err;
}
}
if (cfg->hp_outs) {
if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
mutenid = cfg->hp_pins[0];
else
mutenid = spec->multiout.dac_nids[i];
err = add_out_switch(codec, mutenid, "Headphone");
if (err < 0)
return err;
if (spec->hp_dac) {
err = add_out_volume(codec, spec->hp_dac, "Headphone");
if (err < 0)
return err;
}
}
for (i = 0; i < spec->num_inputs; i++) {
const char *label = spec->input_labels[i];
if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
mutenid = spec->input_pins[i];
else
mutenid = spec->adcs[i];
err = add_in_switch(codec, mutenid, label);
if (err < 0)
return err;
err = add_in_volume(codec, spec->adcs[i], label);
if (err < 0)
return err;
}
if (spec->dig_out) {
err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
if (err < 0)
return err;
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
if (err < 0)
return err;
spec->multiout.share_spdif = 1;
}
if (spec->dig_in) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
if (err < 0)
return err;
err = add_in_volume(codec, spec->dig_in, "IEC958");
}
return 0;
}
/*
*/
static struct hda_pcm_stream ca0110_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
.ops = {
.open = ca0110_playback_pcm_open,
.prepare = ca0110_playback_pcm_prepare,
.cleanup = ca0110_playback_pcm_cleanup
},
};
static struct hda_pcm_stream ca0110_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.ops = {
.prepare = ca0110_capture_pcm_prepare,
.cleanup = ca0110_capture_pcm_cleanup
},
};
static struct hda_pcm_stream ca0110_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.ops = {
.open = ca0110_dig_playback_pcm_open,
.close = ca0110_dig_playback_pcm_close,
.prepare = ca0110_dig_playback_pcm_prepare
},
};
static struct hda_pcm_stream ca0110_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
};
static int ca0110_build_pcms(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
codec->pcm_info = info;
codec->num_pcms = 0;
info->name = "CA0110 Analog";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
codec->num_pcms++;
if (!spec->dig_out && !spec->dig_in)
return 0;
info++;
info->name = "CA0110 Digital";
info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->dig_out) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
ca0110_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
}
if (spec->dig_in) {
info->stream[SNDRV_PCM_STREAM_CAPTURE] =
ca0110_pcm_digital_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
}
codec->num_pcms++;
return 0;
}
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
if (pin) {
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
}
if (dac)
snd_hda_codec_write(codec, dac, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
}
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
if (pin) {
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
if (adc)
snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
static int ca0110_init(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
for (i = 0; i < spec->multiout.num_dacs; i++)
init_output(codec, spec->out_pins[i],
spec->multiout.dac_nids[i]);
init_output(codec, cfg->hp_pins[0], spec->hp_dac);
init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
for (i = 0; i < spec->num_inputs; i++)
init_input(codec, spec->input_pins[i], spec->adcs[i]);
init_input(codec, cfg->dig_in_pin, spec->dig_in);
return 0;
}
static void ca0110_free(struct hda_codec *codec)
{
kfree(codec->spec);
}
static struct hda_codec_ops ca0110_patch_ops = {
.build_controls = ca0110_build_controls,
.build_pcms = ca0110_build_pcms,
.init = ca0110_init,
.free = ca0110_free,
};
static void parse_line_outs(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i, n;
unsigned int def_conf;
hda_nid_t nid;
n = 0;
for (i = 0; i < cfg->line_outs; i++) {
nid = cfg->line_out_pins[i];
def_conf = snd_hda_codec_get_pincfg(codec, nid);
if (!def_conf)
continue; /* invalid pin */
if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
continue;
spec->out_pins[n++] = nid;
}
spec->multiout.dac_nids = spec->dacs;
spec->multiout.num_dacs = n;
spec->multiout.max_channels = n * 2;
}
static void parse_hp_out(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
unsigned int def_conf;
hda_nid_t nid, dac;
if (!cfg->hp_outs)
return;
nid = cfg->hp_pins[0];
def_conf = snd_hda_codec_get_pincfg(codec, nid);
if (!def_conf) {
cfg->hp_outs = 0;
return;
}
if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
return;
for (i = 0; i < cfg->line_outs; i++)
if (dac == spec->dacs[i])
break;
if (i >= cfg->line_outs) {
spec->hp_dac = dac;
spec->multiout.hp_nid = dac;
}
}
static void parse_input(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid, pin;
int n, i, j;
n = 0;
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int type = (wcaps & AC_WCAP_TYPE) >>
AC_WCAP_TYPE_SHIFT;
if (type != AC_WID_AUD_IN)
continue;
if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
continue;
if (pin == cfg->dig_in_pin) {
spec->dig_in = nid;
continue;
}
for (j = 0; j < AUTO_PIN_LAST; j++)
if (cfg->input_pins[j] == pin)
break;
if (j >= AUTO_PIN_LAST)
continue;
spec->input_pins[n] = pin;
spec->input_labels[n] = auto_pin_cfg_labels[j];
spec->adcs[n] = nid;
n++;
}
spec->num_inputs = n;
}
static void parse_digital(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
if (cfg->dig_outs &&
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
&spec->dig_out, 1) == 1)
spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
}
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
int err;
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
parse_line_outs(codec);
parse_hp_out(codec);
parse_digital(codec);
parse_input(codec);
return 0;
}
int patch_ca0110(struct hda_codec *codec)
{
struct ca0110_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
codec->bus->needs_damn_long_delay = 1;
err = ca0110_parse_auto_config(codec);
if (err < 0)
goto error;
codec->patch_ops = ca0110_patch_ops;
return 0;
error:
kfree(codec->spec);
codec->spec = NULL;
return err;
}
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_ca0110[] = {
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:1102000a");
MODULE_ALIAS("snd-hda-codec-id:1102000b");
MODULE_ALIAS("snd-hda-codec-id:1102000d");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_preset_list ca0110_list = {
.preset = snd_hda_preset_ca0110,
.owner = THIS_MODULE,
};
static int __init patch_ca0110_init(void)
{
return snd_hda_add_codec_preset(&ca0110_list);
}
static void __exit patch_ca0110_exit(void)
{
snd_hda_delete_codec_preset(&ca0110_list);
}
module_init(patch_ca0110_init)
module_exit(patch_ca0110_exit)

View File

@ -35,9 +35,28 @@ struct nvhdmi_spec {
struct hda_pcm pcm_rec;
};
#define Nv_VERB_SET_Channel_Allocation 0xF79
#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
#define Nv_VERB_SET_Audio_Protection_On 0xF98
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
#define Nv_Master_Convert_nid 0x04
#define Nv_Master_Pin_nid 0x05
static hda_nid_t nvhdmi_convert_nids[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
static struct hda_verb nvhdmi_basic_init[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{} /* terminator */
};
@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec)
* Digital out
*/
static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct nvhdmi_spec *spec = codec->spec;
int i;
snd_hda_codec_write(codec, Nv_Master_Convert_nid,
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
for (i = 0; i < 4; i++) {
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
/* set the stream format */
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
AC_VERB_SET_STREAM_FORMAT, 0);
}
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
int chs;
unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
int i;
mutex_lock(&codec->spdif_mutex);
chs = substream->runtime->channels;
chan = chs ? (chs - 1) : 1;
switch (chs) {
default:
case 0:
case 2:
chanmask = 0x00;
break;
case 4:
chanmask = 0x08;
break;
case 6:
chanmask = 0x0b;
break;
case 8:
chanmask = 0x13;
break;
}
dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
/* set the Audio InforFrame Channel Allocation */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Channel_Allocation, chanmask);
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
Nv_Master_Convert_nid,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
/* set the stream format */
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
AC_VERB_SET_STREAM_FORMAT, format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
Nv_Master_Convert_nid,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
Nv_Master_Convert_nid,
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
for (i = 0; i < 4; i++) {
if (chs == 2)
channel_id = 0;
else
channel_id = i * 2;
/* turn off SPDIF once;
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_convert_nids[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_convert_nids[i],
0,
AC_VERB_SET_CHANNEL_STREAMID,
(stream_tag << 4) | channel_id);
/* set the stream format */
snd_hda_codec_write(codec,
nvhdmi_convert_nids[i],
0,
AC_VERB_SET_STREAM_FORMAT,
format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_convert_nids[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_convert_nids[i],
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
}
/* set the Audio Info Frame Checksum */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Info_Frame_Checksum,
(0x71 - chan - chanmask));
mutex_unlock(&codec->spdif_mutex);
return 0;
}
static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
format, substream);
format, substream);
}
static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = 0x4, /* NID to query formats and rates and setup streams */
.channels_max = 8,
.nid = Nv_Master_Convert_nid,
.rates = SNDRV_PCM_RATE_48000,
.maxbps = 16,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.ops = {
.open = nvhdmi_dig_playback_pcm_open,
.close = nvhdmi_dig_playback_pcm_close,
.prepare = nvhdmi_dig_playback_pcm_prepare
.close = nvhdmi_dig_playback_pcm_close_8ch,
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
},
};
static int nvhdmi_build_pcms(struct hda_codec *codec)
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = Nv_Master_Convert_nid,
.rates = SNDRV_PCM_RATE_48000,
.maxbps = 16,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.ops = {
.open = nvhdmi_dig_playback_pcm_open,
.close = nvhdmi_dig_playback_pcm_close_2ch,
.prepare = nvhdmi_dig_playback_pcm_prepare_2ch
},
};
static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
{
struct nvhdmi_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm_rec;
@ -117,7 +293,24 @@ static int nvhdmi_build_pcms(struct hda_codec *codec)
info->name = "NVIDIA HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
= nvhdmi_pcm_digital_playback_8ch;
return 0;
}
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
{
struct nvhdmi_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm_rec;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "NVIDIA HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
= nvhdmi_pcm_digital_playback_2ch;
return 0;
}
@ -127,14 +320,21 @@ static void nvhdmi_free(struct hda_codec *codec)
kfree(codec->spec);
}
static struct hda_codec_ops nvhdmi_patch_ops = {
static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
.build_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms,
.build_pcms = nvhdmi_build_pcms_8ch,
.init = nvhdmi_init,
.free = nvhdmi_free,
};
static int patch_nvhdmi(struct hda_codec *codec)
static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
.build_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms_2ch,
.init = nvhdmi_init,
.free = nvhdmi_free,
};
static int patch_nvhdmi_8ch(struct hda_codec *codec)
{
struct nvhdmi_spec *spec;
@ -144,13 +344,30 @@ static int patch_nvhdmi(struct hda_codec *codec)
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
* seems to be unused in pure-digital
* case. */
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 8;
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
codec->patch_ops = nvhdmi_patch_ops;
codec->patch_ops = nvhdmi_patch_ops_8ch;
return 0;
}
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct nvhdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
codec->patch_ops = nvhdmi_patch_ops_2ch;
return 0;
}
@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{} /* terminator */
};

File diff suppressed because it is too large Load Diff

View File

@ -100,6 +100,7 @@ enum {
STAC_HP_M4,
STAC_HP_DV5,
STAC_HP_HDX,
STAC_HP_DV4_1222NR,
STAC_92HD71BXX_MODELS
};
@ -193,6 +194,7 @@ struct sigmatel_spec {
unsigned int gpio_dir;
unsigned int gpio_data;
unsigned int gpio_mute;
unsigned int gpio_led;
/* stream */
unsigned int stream_delay;
@ -634,6 +636,40 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
return 0;
}
static unsigned int stac92xx_vref_set(struct hda_codec *codec,
hda_nid_t nid, unsigned int new_vref)
{
unsigned int error;
unsigned int pincfg;
pincfg = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
pincfg &= 0xff;
pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
pincfg |= new_vref;
if (new_vref == AC_PINCTL_VREF_HIZ)
pincfg |= AC_PINCTL_OUT_EN;
else
pincfg |= AC_PINCTL_IN_EN;
error = snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
if (error < 0)
return error;
else
return 1;
}
static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int vref;
vref = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
vref &= AC_PINCTL_VREFEN;
return vref;
}
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@ -995,6 +1031,17 @@ static struct hda_verb stac9205_core_init[] = {
.private_value = verb_read | (verb_write << 16), \
}
#define DC_BIAS(xname, idx, nid) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = idx, \
.info = stac92xx_dc_bias_info, \
.get = stac92xx_dc_bias_get, \
.put = stac92xx_dc_bias_put, \
.private_value = nid, \
}
static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
@ -1837,6 +1884,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
[STAC_HP_M4] = NULL,
[STAC_HP_DV5] = NULL,
[STAC_HP_HDX] = NULL,
[STAC_HP_DV4_1222NR] = NULL,
};
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
@ -1848,6 +1896,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
[STAC_HP_M4] = "hp-m4",
[STAC_HP_DV5] = "hp-dv5",
[STAC_HP_HDX] = "hp-hdx",
[STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
};
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
@ -1856,6 +1905,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
"HP dv4-1222nr", STAC_HP_DV4_1222NR),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
"HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@ -2545,7 +2596,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
return 0;
}
static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
hda_nid_t nid)
{
unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
@ -2599,15 +2651,108 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
return 1;
}
#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int i;
static char *texts[] = {
"Mic In", "Line In", "Line Out"
};
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = kcontrol->private_value;
if (nid == spec->mic_switch || nid == spec->line_switch)
i = 3;
else
i = 2;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->value.enumerated.items = i;
uinfo->count = 1;
if (uinfo->value.enumerated.item >= i)
uinfo->value.enumerated.item = i-1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
unsigned int vref = stac92xx_vref_get(codec, nid);
if (vref == stac92xx_get_default_vref(codec, nid))
ucontrol->value.enumerated.item[0] = 0;
else if (vref == AC_PINCTL_VREF_GRD)
ucontrol->value.enumerated.item[0] = 1;
else if (vref == AC_PINCTL_VREF_HIZ)
ucontrol->value.enumerated.item[0] = 2;
return 0;
}
static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int new_vref = 0;
unsigned int error;
hda_nid_t nid = kcontrol->private_value;
if (ucontrol->value.enumerated.item[0] == 0)
new_vref = stac92xx_get_default_vref(codec, nid);
else if (ucontrol->value.enumerated.item[0] == 1)
new_vref = AC_PINCTL_VREF_GRD;
else if (ucontrol->value.enumerated.item[0] == 2)
new_vref = AC_PINCTL_VREF_HIZ;
else
return 0;
if (new_vref != stac92xx_vref_get(codec, nid)) {
error = stac92xx_vref_set(codec, nid, new_vref);
return error;
}
return 0;
}
static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[2];
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
if (kcontrol->private_value == spec->line_switch)
texts[0] = "Line In";
else
texts[0] = "Mic In";
texts[1] = "Line Out";
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->value.enumerated.items = 2;
uinfo->count = 1;
if (uinfo->value.enumerated.item >= 2)
uinfo->value.enumerated.item = 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
int io_idx = kcontrol-> private_value & 0xff;
hda_nid_t nid = kcontrol->private_value;
int io_idx = (nid == spec->mic_switch) ? 1 : 0;
ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
return 0;
}
@ -2615,9 +2760,9 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = kcontrol->private_value >> 8;
int io_idx = kcontrol-> private_value & 0xff;
unsigned short val = !!ucontrol->value.integer.value[0];
hda_nid_t nid = kcontrol->private_value;
int io_idx = (nid == spec->mic_switch) ? 1 : 0;
unsigned short val = !!ucontrol->value.enumerated.item[0];
spec->io_switch[io_idx] = val;
@ -2626,7 +2771,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
else {
unsigned int pinctl = AC_PINCTL_IN_EN;
if (io_idx) /* set VREF for mic */
pinctl |= stac92xx_get_vref(codec, nid);
pinctl |= stac92xx_get_default_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
@ -2707,7 +2852,8 @@ enum {
STAC_CTL_WIDGET_AMP_VOL,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
STAC_CTL_WIDGET_CLFE_SWITCH
STAC_CTL_WIDGET_CLFE_SWITCH,
STAC_CTL_WIDGET_DC_BIAS
};
static struct snd_kcontrol_new stac92xx_control_templates[] = {
@ -2719,6 +2865,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
DC_BIAS(NULL, 0, 0),
};
/* add dynamic controls */
@ -2782,6 +2929,34 @@ static struct snd_kcontrol_new stac_input_src_temp = {
.put = stac92xx_mux_enum_put,
};
static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
hda_nid_t nid, int idx)
{
int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int control = 0;
struct sigmatel_spec *spec = codec->spec;
char name[22];
if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
&& nid == spec->line_switch)
control = STAC_CTL_WIDGET_IO_SWITCH;
else if (snd_hda_query_pin_caps(codec, nid)
& (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
control = STAC_CTL_WIDGET_DC_BIAS;
else if (nid == spec->mic_switch)
control = STAC_CTL_WIDGET_IO_SWITCH;
}
if (control) {
strcpy(name, auto_pin_cfg_labels[idx]);
return stac92xx_add_control(codec->spec, control,
strcat(name, " Jack Mode"), nid);
}
return 0;
}
static int stac92xx_add_input_source(struct sigmatel_spec *spec)
{
struct snd_kcontrol_new *knew;
@ -3144,7 +3319,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid;
int err;
int idx;
err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
spec->multiout.dac_nids,
@ -3161,20 +3338,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err;
}
if (spec->line_switch) {
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
"Line In as Output Switch",
spec->line_switch << 8);
if (err < 0)
return err;
}
if (spec->mic_switch) {
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
"Mic as Output Switch",
(spec->mic_switch << 8) | 1);
if (err < 0)
return err;
for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
nid = cfg->input_pins[idx];
if (nid) {
err = stac92xx_add_jack_mode_control(codec, nid, idx);
if (err < 0)
return err;
}
}
return 0;
@ -3639,6 +3809,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
err = snd_hda_attach_beep_device(codec, nid);
if (err < 0)
return err;
/* IDT/STAC codecs have linear beep tone parameter */
codec->beep->linear_tone = 1;
/* if no beep switch is available, make its own one */
caps = query_amp_caps(codec, nid, HDA_OUTPUT);
if (codec->beep &&
@ -4082,7 +4254,7 @@ static int stac92xx_init(struct hda_codec *codec)
unsigned int pinctl, conf;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
/* for mic pins, force to initialize */
pinctl = stac92xx_get_vref(codec, nid);
pinctl = stac92xx_get_default_vref(codec, nid);
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
@ -4535,17 +4707,19 @@ static int stac92xx_resume(struct hda_codec *codec)
return 0;
}
/*
* using power check for controlling mute led of HP HDX notebooks
* using power check for controlling mute led of HP notebooks
* check for mute state only on Speakers (nid = 0x10)
*
* For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
* the LED is NOT working properly !
*
* Changed name to reflect that it now works for any designated
* model, not just HP HDX.
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
static int stac92xx_hp_check_power_status(struct hda_codec *codec,
hda_nid_t nid)
{
struct sigmatel_spec *spec = codec->spec;
@ -4553,9 +4727,9 @@ static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
if (nid == 0x10) {
if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
HDA_AMP_MUTE)
spec->gpio_data &= ~0x08; /* orange */
spec->gpio_data &= ~spec->gpio_led; /* orange */
else
spec->gpio_data |= 0x08; /* white */
spec->gpio_data |= spec->gpio_led; /* white */
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir,
@ -5201,6 +5375,15 @@ again:
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
/* Some HP machines seem to have unstable codec communications
* especially with ATI fglrx driver. For recovering from the
* CORB/RIRB stall, allow the BUS reset and keep always sync
*/
if (spec->board_config == STAC_HP_DV5) {
codec->bus->sync_write = 1;
codec->bus->allow_bus_reset = 1;
}
spec->aloopback_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
@ -5234,6 +5417,15 @@ again:
spec->num_smuxes = 0;
spec->num_dmuxes = 1;
break;
case STAC_HP_DV4_1222NR:
spec->num_dmics = 1;
/* I don't know if it needs 1 or 2 smuxes - will wait for
* bug reports to fix if needed
*/
spec->num_smuxes = 1;
spec->num_dmuxes = 1;
spec->gpio_led = 0x01;
/* fallthrough */
case STAC_HP_DV5:
snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
@ -5242,22 +5434,21 @@ again:
spec->num_dmics = 1;
spec->num_dmuxes = 1;
spec->num_smuxes = 1;
/*
* For controlling MUTE LED on HP HDX16/HDX18 notebooks,
* the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* orange/white mute led on GPIO3, orange=0, white=1 */
spec->gpio_mask |= 0x08;
spec->gpio_dir |= 0x08;
spec->gpio_data |= 0x08; /* set to white */
spec->gpio_led = 0x08;
break;
}
#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 =
stac92xx_hp_hdx_check_power_status;
stac92xx_hp_check_power_status;
}
#endif
break;
};
spec->multiout.dac_nids = spec->dac_nids;
if (spec->dinput_mux)
@ -5282,7 +5473,7 @@ again:
codec->proc_widget_hook = stac92hd7x_proc_hook;
return 0;
};
}
static int patch_stac922x(struct hda_codec *codec)
{
@ -5437,7 +5628,7 @@ static int patch_stac927x(struct hda_codec *codec)
/* correct the device field to SPDIF out */
snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
break;
};
}
/* configure the analog microphone on some laptops */
snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */

View File

@ -205,7 +205,7 @@ struct via_spec {
/* playback */
struct hda_multi_out multiout;
hda_nid_t extra_dig_out_nid;
hda_nid_t slave_dig_outs[2];
/* capture */
unsigned int num_adc_nids;
@ -731,21 +731,6 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
/* setup SPDIF output stream */
static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
unsigned int stream_tag, unsigned int format)
{
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
if (codec->spdif_ctls & AC_DIG1_ENABLE)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
/* turn on again (if needed) */
if (codec->spdif_ctls & AC_DIG1_ENABLE)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
}
static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@ -753,19 +738,16 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
hda_nid_t nid;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
stream_tag, format, substream);
}
/* 1st or 2nd S/PDIF */
if (substream->number == 0)
nid = spec->multiout.dig_out_nid;
else if (substream->number == 1)
nid = spec->extra_dig_out_nid;
else
return -1;
mutex_lock(&codec->spdif_mutex);
setup_dig_playback_stream(codec, nid, stream_tag, format);
mutex_unlock(&codec->spdif_mutex);
static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct via_spec *spec = codec->spec;
snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
return 0;
}
@ -842,7 +824,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = {
.ops = {
.open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close,
.prepare = via_dig_playback_pcm_prepare
.prepare = via_dig_playback_pcm_prepare,
.cleanup = via_dig_playback_pcm_cleanup
},
};
@ -874,13 +857,6 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
spec->multiout.share_spdif = 1;
if (spec->extra_dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec,
spec->extra_dig_out_nid);
if (err < 0)
return err;
}
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@ -1013,10 +989,6 @@ static void via_unsol_event(struct hda_codec *codec,
via_gpio_control(codec);
}
static hda_nid_t slave_dig_outs[] = {
0,
};
static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@ -1051,8 +1023,9 @@ static int via_init(struct hda_codec *codec)
snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
/* no slave outs */
codec->slave_dig_outs = slave_dig_outs;
/* assign slave outs */
if (spec->slave_dig_outs[0])
codec->slave_dig_outs = spec->slave_dig_outs;
return 0;
}
@ -2134,7 +2107,8 @@ static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
.ops = {
.open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close,
.prepare = via_dig_playback_pcm_prepare
.prepare = via_dig_playback_pcm_prepare,
.cleanup = via_dig_playback_pcm_cleanup
},
};
@ -2589,14 +2563,15 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
};
static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
.substreams = 2,
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
/* NID is set in via_build_pcms */
.ops = {
.open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close,
.prepare = via_dig_playback_pcm_prepare
.prepare = via_dig_playback_pcm_prepare,
.cleanup = via_dig_playback_pcm_cleanup
},
};
@ -2805,14 +2780,37 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
return 0;
}
/* fill out digital output widgets; one for master and one for slave outputs */
static void fill_dig_outs(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->autocfg.dig_outs; i++) {
hda_nid_t nid;
int conn;
nid = spec->autocfg.dig_out_pins[i];
if (!nid)
continue;
conn = snd_hda_get_connections(codec, nid, &nid, 1);
if (conn < 1)
continue;
if (!spec->multiout.dig_out_nid)
spec->multiout.dig_out_nid = nid;
else {
spec->slave_dig_outs[0] = nid;
break; /* at most two dig outs */
}
}
}
static int vt1708S_parse_auto_config(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int err;
static hda_nid_t vt1708s_ignore[] = {0x21, 0};
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
vt1708s_ignore);
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
@ -2833,10 +2831,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
spec->extra_dig_out_nid = 0x15;
fill_dig_outs(codec);
if (spec->kctls.list)
spec->mixers[spec->num_mixers++] = spec->kctls.list;
@ -3000,7 +2995,8 @@ static struct hda_pcm_stream vt1702_pcm_digital_playback = {
.ops = {
.open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close,
.prepare = via_dig_playback_pcm_prepare
.prepare = via_dig_playback_pcm_prepare,
.cleanup = via_dig_playback_pcm_cleanup
},
};
@ -3128,10 +3124,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int err;
static hda_nid_t vt1702_ignore[] = {0x1C, 0};
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
vt1702_ignore);
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
@ -3152,10 +3146,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
spec->extra_dig_out_nid = 0x1B;
fill_dig_outs(codec);
if (spec->kctls.list)
spec->mixers[spec->num_mixers++] = spec->kctls.list;