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

View File

@ -139,6 +139,19 @@ config SND_HDA_CODEC_CONEXANT
snd-hda-codec-conexant. snd-hda-codec-conexant.
This module is automatically loaded at probing. 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 config SND_HDA_CODEC_CMEDIA
bool "Build C-Media HD-audio codec support" bool "Build C-Media HD-audio codec support"
default y 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-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-atihdmi-objs := patch_atihdmi.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-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
@ -40,6 +41,9 @@ endif
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
endif endif
ifdef CONFIG_SND_HDA_CODEC_CA0110
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
endif
ifdef CONFIG_SND_HDA_CODEC_CONEXANT ifdef CONFIG_SND_HDA_CODEC_CONEXANT
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
endif endif

View File

@ -45,6 +45,46 @@ static void snd_hda_generate_beep(struct work_struct *work)
AC_VERB_SET_BEEP_CONTROL, beep->tone); 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, static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz) unsigned int code, int hz)
{ {
@ -55,21 +95,14 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
if (hz) if (hz)
hz = 1000; hz = 1000;
case SND_TONE: case SND_TONE:
hz *= 1000; /* fixed point */ if (beep->linear_tone)
hz = hz - DIGBEEP_HZ_MIN; beep->tone = beep_linear_tone(beep, hz);
if (hz < 0) else
hz = 0; /* turn off PC beep*/ beep->tone = beep_standard_tone(beep, hz);
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
hz = 0xff;
else {
hz /= DIGBEEP_HZ_STEP;
hz++;
}
break; break;
default: default:
return -1; return -1;
} }
beep->tone = hz;
/* schedule beep event */ /* schedule beep event */
schedule_work(&beep->beep_work); schedule_work(&beep->beep_work);

View File

@ -30,8 +30,9 @@ struct hda_beep {
struct hda_codec *codec; struct hda_codec *codec;
char phys[32]; char phys[32];
int tone; int tone;
int nid; hda_nid_t nid;
int enabled; 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 */ 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" }, { 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" }, { 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" }, { 0x10ec, "Realtek" },
{ 0x1102, "Creative" },
{ 0x1106, "VIA" }, { 0x1106, "VIA" },
{ 0x111d, "IDT" }, { 0x111d, "IDT" },
{ 0x11c1, "LSI" }, { 0x11c1, "LSI" },
@ -157,6 +158,39 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
return val; 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 * snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec * @codec: the HDA codec
@ -173,18 +207,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int direct, int direct,
unsigned int verb, unsigned int parm) 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; unsigned int res;
codec_exec_verb(codec, cmd, &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);
return res; return res;
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_read); 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, int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int parm) 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; unsigned int res;
int err; return codec_exec_verb(codec, cmd,
codec->bus->sync_write ? &res : NULL);
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;
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_write); 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 struct hda_vendor_id *c;
const char *vendor = NULL; const char *vendor = NULL;
u16 vendor_id = codec->vendor_id >> 16; 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++) { for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) { if (c->id == vendor_id) {
@ -625,14 +646,21 @@ static int get_codec_name(struct hda_codec *codec)
sprintf(tmp, "Generic %04x", vendor_id); sprintf(tmp, "Generic %04x", vendor_id);
vendor = tmp; 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) if (codec->preset && codec->preset->name)
snprintf(name, sizeof(name), "%s %s", vendor, codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
codec->preset->name); else {
else sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
snprintf(name, sizeof(name), "%s ID %x", vendor, codec->chip_name = kstrdup(tmp, GFP_KERNEL);
codec->vendor_id & 0xffff); }
codec->name = kstrdup(name, GFP_KERNEL); if (!codec->chip_name)
if (!codec->name)
return -ENOMEM; return -ENOMEM;
return 0; return 0;
} }
@ -838,7 +866,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
module_put(codec->owner); module_put(codec->owner);
free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache); free_hda_cache(&codec->cmd_cache);
kfree(codec->name); kfree(codec->vendor_name);
kfree(codec->chip_name);
kfree(codec->modelname); kfree(codec->modelname);
kfree(codec->wcaps); kfree(codec->wcaps);
kfree(codec); kfree(codec);
@ -979,15 +1008,16 @@ int snd_hda_codec_configure(struct hda_codec *codec)
int err; int err;
codec->preset = find_codec_preset(codec); codec->preset = find_codec_preset(codec);
if (!codec->name) { if (!codec->vendor_name || !codec->chip_name) {
err = get_codec_name(codec); err = get_codec_name(codec);
if (err < 0) if (err < 0)
return err; return err;
} }
/* audio codec should override the mixer name */ /* audio codec should override the mixer name */
if (codec->afg || !*codec->bus->card->mixername) if (codec->afg || !*codec->bus->card->mixername)
strlcpy(codec->bus->card->mixername, codec->name, snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername)); sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
if (is_generic_config(codec)) { if (is_generic_config(codec)) {
err = snd_hda_parse_generic_codec(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? */ /* FIXME: more better hash key? */
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #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_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_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) #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); 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; 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) if (!info)
return 0; return 0;
if (!info->head.val) { if (!info->head.val) {
info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
info->head.val |= INFO_AMP_CAPS; info->head.val |= INFO_AMP_CAPS;
info->amp_caps = func(codec, nid);
} }
return info->amp_caps; 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); 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)); memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
id.index = idx; id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
strcpy(id.name, name); strcpy(id.name, name);
return snd_ctl_find_id(codec->bus->card, &id); 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 snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm) int direct, unsigned int verb, unsigned int parm)
{ {
struct hda_bus *bus = codec->bus; int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
unsigned int res; struct hda_cache_head *c;
int err; u32 key;
res = make_codec_cmd(codec, nid, direct, verb, parm); if (err < 0)
snd_hda_power_up(codec); return err;
mutex_lock(&bus->cmd_mutex); /* parm may contain the verb stuff for get/set amp */
err = bus->ops.command(bus, res); verb = verb | (parm >> 8);
if (!err) { parm &= 0xff;
struct hda_cache_head *c; key = build_cmd_cache_key(nid, verb);
u32 key; mutex_lock(&codec->bus->cmd_mutex);
/* parm may contain the verb stuff for get/set amp */ c = get_alloc_hash(&codec->cmd_cache, key);
verb = verb | (parm >> 8); if (c)
parm &= 0xff; c->val = parm;
key = build_cmd_cache_key(nid, verb); mutex_unlock(&codec->bus->cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key); return 0;
if (c)
c->val = parm;
}
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); 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) { if (wcaps & AC_WCAP_POWER) {
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
AC_WCAP_TYPE_SHIFT; AC_WCAP_TYPE_SHIFT;
if (wid_type == AC_WID_PIN) { if (power_state == AC_PWRST_D3 &&
wid_type == AC_WID_PIN) {
unsigned int pincap; unsigned int pincap;
/* /*
* don't power down the widget if it controls * 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, nid, 0,
AC_VERB_GET_EAPD_BTLENABLE, 0); AC_VERB_GET_EAPD_BTLENABLE, 0);
eapd &= 0x02; eapd &= 0x02;
if (power_state == AC_PWRST_D3 && eapd) if (eapd)
continue; continue;
} }
} }
@ -2544,6 +2586,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
} }
EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); 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 * snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec * @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; unsigned int i, val, wcaps;
val = 0;
wcaps = get_wcaps(codec, nid); wcaps = get_wcaps(codec, nid);
if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) { val = query_pcm_param(codec, nid);
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);
if (ratesp) { if (ratesp) {
u32 rates = 0; 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; u64 formats = 0;
unsigned int streams, bps; unsigned int streams, bps;
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); streams = query_stream_param(codec, nid);
if (streams == -1) if (!streams)
return -EIO; return -EIO;
if (!streams) {
streams = snd_hda_param_read(codec, codec->afg,
AC_PAR_STREAM);
if (streams == -1)
return -EIO;
}
bps = 0; bps = 0;
if (streams & AC_SUPFMT_PCM) { 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; int i;
unsigned int val = 0, rate, stream; unsigned int val = 0, rate, stream;
if (nid != codec->afg && val = query_pcm_param(codec, nid);
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { if (!val)
val = snd_hda_param_read(codec, nid, AC_PAR_PCM); return 0;
if (val == -1)
return 0;
}
if (!val) {
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
if (val == -1)
return 0;
}
rate = format & 0xff00; rate = format & 0xff00;
for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) 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) if (i >= AC_PAR_PCM_RATE_BITS)
return 0; return 0;
stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); stream = query_stream_param(codec, nid);
if (stream == -1) if (!stream)
return 0;
if (!stream && nid != codec->afg)
stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (!stream || stream == -1)
return 0; return 0;
if (stream & AC_SUPFMT_PCM) { if (stream & AC_SUPFMT_PCM) {
@ -3835,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
/** /**
* snd_hda_suspend - suspend the codecs * snd_hda_suspend - suspend the codecs
* @bus: the HDA bus * @bus: the HDA bus
* @state: suspsend state
* *
* Returns 0 if successful. * 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; struct hda_codec *codec;

View File

@ -574,6 +574,8 @@ struct hda_bus_ops {
/* attach a PCM stream */ /* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm); struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */ /* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus); void (*pm_notify)(struct hda_bus *bus);
@ -622,7 +624,13 @@ struct hda_bus {
/* misc op flags */ /* misc op flags */
unsigned int needs_damn_long_delay :1; 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 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 */ /* detected preset */
const struct hda_codec_preset *preset; const struct hda_codec_preset *preset;
struct module *owner; 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 */ const char *modelname; /* model name for preset */
/* set by patch */ /* set by patch */
@ -905,7 +914,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
* power management * power management
*/ */
#ifdef CONFIG_PM #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); int snd_hda_resume(struct hda_bus *bus);
#endif #endif

View File

@ -242,7 +242,8 @@ CODEC_INFO_SHOW(subsystem_id);
CODEC_INFO_SHOW(revision_id); CODEC_INFO_SHOW(revision_id);
CODEC_INFO_SHOW(afg); CODEC_INFO_SHOW(afg);
CODEC_INFO_SHOW(mfg); 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); CODEC_INFO_STR_SHOW(modelname);
#define CODEC_INFO_STORE(type) \ #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(vendor_id);
CODEC_INFO_STORE(subsystem_id); CODEC_INFO_STORE(subsystem_id);
CODEC_INFO_STORE(revision_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); CODEC_INFO_STR_STORE(modelname);
#define CODEC_ACTION_STORE(type) \ #define CODEC_ACTION_STORE(type) \
@ -499,7 +501,8 @@ static struct device_attribute codec_attrs[] = {
CODEC_ATTR_RW(revision_id), CODEC_ATTR_RW(revision_id),
CODEC_ATTR_RO(afg), CODEC_ATTR_RO(afg),
CODEC_ATTR_RO(mfg), 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(modelname),
CODEC_ATTR_RW(init_verbs), CODEC_ATTR_RW(init_verbs),
CODEC_ATTR_RW(hints), CODEC_ATTR_RW(hints),

View File

@ -128,21 +128,33 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{ULI, M5461}}"); "{ULI, M5461}}");
MODULE_DESCRIPTION("Intel HDA driver"); MODULE_DESCRIPTION("Intel HDA driver");
#ifdef CONFIG_SND_VERBOSE_PRINTK
#define SFX /* nop */
#else
#define SFX "hda-intel: " #define SFX "hda-intel: "
#endif
/* /*
* registers * registers
*/ */
#define ICH6_REG_GCAP 0x00 #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_VMIN 0x02
#define ICH6_REG_VMAJ 0x03 #define ICH6_REG_VMAJ 0x03
#define ICH6_REG_OUTPAY 0x04 #define ICH6_REG_OUTPAY 0x04
#define ICH6_REG_INPAY 0x06 #define ICH6_REG_INPAY 0x06
#define ICH6_REG_GCTL 0x08 #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_WAKEEN 0x0c
#define ICH6_REG_STATESTS 0x0e #define ICH6_REG_STATESTS 0x0e
#define ICH6_REG_GSTS 0x10 #define ICH6_REG_GSTS 0x10
#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
#define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTCTL 0x20
#define ICH6_REG_INTSTS 0x24 #define ICH6_REG_INTSTS 0x24
#define ICH6_REG_WALCLK 0x30 #define ICH6_REG_WALCLK 0x30
@ -150,17 +162,27 @@ MODULE_DESCRIPTION("Intel HDA driver");
#define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBLBASE 0x40
#define ICH6_REG_CORBUBASE 0x44 #define ICH6_REG_CORBUBASE 0x44
#define ICH6_REG_CORBWP 0x48 #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_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_REG_CORBSTS 0x4d
#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define ICH6_REG_CORBSIZE 0x4e #define ICH6_REG_CORBSIZE 0x4e
#define ICH6_REG_RIRBLBASE 0x50 #define ICH6_REG_RIRBLBASE 0x50
#define ICH6_REG_RIRBUBASE 0x54 #define ICH6_REG_RIRBUBASE 0x54
#define ICH6_REG_RIRBWP 0x58 #define ICH6_REG_RIRBWP 0x58
#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
#define ICH6_REG_RINTCNT 0x5a #define ICH6_REG_RINTCNT 0x5a
#define ICH6_REG_RIRBCTL 0x5c #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_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_RIRBSIZE 0x5e
#define ICH6_REG_IC 0x60 #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_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define ICH6_INT_GLOBAL_EN 0x80000000 /* global 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 */ /* below are so far hardcoded - should read registers in future */
#define ICH6_MAX_CORB_ENTRIES 256 #define ICH6_MAX_CORB_ENTRIES 256
#define ICH6_MAX_RIRB_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 */ /* set the corb write pointer to 0 */
azx_writew(chip, CORBWP, 0); azx_writew(chip, CORBWP, 0);
/* reset the corb hw read pointer */ /* reset the corb hw read pointer */
azx_writew(chip, CORBRP, ICH6_RBRWP_CLR); azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
/* enable corb dma */ /* enable corb dma */
azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN); azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
/* RIRB set up */ /* RIRB set up */
chip->rirb.addr = chip->rb.addr + 2048; chip->rirb.addr = chip->rb.addr + 2048;
chip->rirb.buf = (u32 *)(chip->rb.area + 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, RIRBLBASE, (u32)chip->rirb.addr);
azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
/* set the rirb size to 256 entries (ULI requires explicitly) */ /* set the rirb size to 256 entries (ULI requires explicitly) */
azx_writeb(chip, RIRBSIZE, 0x02); azx_writeb(chip, RIRBSIZE, 0x02);
/* reset the rirb hw write pointer */ /* 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 */ /* set N=1, get RIRB response interrupt for new entry */
azx_writew(chip, RINTCNT, 1); azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */ /* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); 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) 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) { if (!chip->rirb.cmds) {
smp_rmb(); smp_rmb();
bus->rirb_error = 0;
return chip->rirb.res; /* the last value */ return chip->rirb.res; /* the last value */
} }
if (time_after(jiffies, timeout)) if (time_after(jiffies, timeout))
@ -619,19 +632,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
} }
if (chip->msi) { 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); "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
free_irq(chip->irq, chip); free_irq(chip->irq, chip);
chip->irq = -1; chip->irq = -1;
pci_disable_msi(chip->pci); pci_disable_msi(chip->pci);
chip->msi = 0; chip->msi = 0;
if (azx_acquire_irq(chip, 1) < 0) if (azx_acquire_irq(chip, 1) < 0) {
bus->rirb_error = 1;
return -1; return -1;
}
goto again; goto again;
} }
if (!chip->polling_mode) { 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", "switching to polling mode: last cmd=0x%08x\n",
chip->last_cmd); chip->last_cmd);
chip->polling_mode = 1; chip->polling_mode = 1;
@ -646,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
return -1; 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, " snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n", "switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd); chip->last_cmd);
chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0;
/* switch to single_cmd mode */
chip->single_cmd = 1; chip->single_cmd = 1;
bus->response_reset = 0;
/* re-initialize CORB/RIRB */
azx_free_cmd_io(chip); azx_free_cmd_io(chip);
azx_init_cmd_io(chip);
return -1; 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. * 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 */ /* send a command */
static int azx_single_send_cmd(struct hda_bus *bus, u32 val) static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
{ {
struct azx *chip = bus->private_data; struct azx *chip = bus->private_data;
int timeout = 50; int timeout = 50;
bus->rirb_error = 0;
while (timeout--) { while (timeout--) {
/* check ICB busy bit */ /* check ICB busy bit */
if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { 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_writel(chip, IC, val);
azx_writew(chip, IRS, azx_readw(chip, IRS) | azx_writew(chip, IRS, azx_readw(chip, IRS) |
ICH6_IRS_BUSY); ICH6_IRS_BUSY);
return 0; return azx_single_wait_for_response(chip);
} }
udelay(1); 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) static unsigned int azx_single_get_response(struct hda_bus *bus)
{ {
struct azx *chip = bus->private_data; struct azx *chip = bus->private_data;
int timeout = 50; return chip->rirb.res;
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;
} }
/* /*
@ -775,17 +810,17 @@ static int azx_reset(struct azx *chip)
/* check to see if controller is ready */ /* check to see if controller is ready */
if (!azx_readb(chip, GCTL)) { if (!azx_readb(chip, GCTL)) {
snd_printd("azx_reset: controller not ready!\n"); snd_printd(SFX "azx_reset: controller not ready!\n");
return -EBUSY; return -EBUSY;
} }
/* Accept unsolicited responses */ /* 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 */ /* detect codecs */
if (!chip->codec_mask) { if (!chip->codec_mask) {
chip->codec_mask = azx_readw(chip, STATESTS); 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; return 0;
@ -895,8 +930,7 @@ static void azx_init_chip(struct azx *chip)
azx_int_enable(chip); azx_int_enable(chip);
/* initialize the codec command I/O */ /* initialize the codec command I/O */
if (!chip->single_cmd) azx_init_cmd_io(chip);
azx_init_cmd_io(chip);
/* program the position buffer */ /* program the position buffer */
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@ -953,12 +987,12 @@ static void azx_init_pci(struct azx *chip)
case AZX_DRIVER_SCH: case AZX_DRIVER_SCH:
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) { 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)); snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
pci_read_config_word(chip->pci, pci_read_config_word(chip->pci,
INTEL_SCH_HDA_DEVC, &snoop); INTEL_SCH_HDA_DEVC, &snoop);
snd_printdd("HDA snoop disabled, enabling ... %s\n",\ snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \ (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
? "Failed" : "OK"); ? "Failed" : "OK");
} }
break; break;
@ -1012,7 +1046,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* clear rirb int */ /* clear rirb int */
status = azx_readb(chip, RIRBSTS); status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) { if (status & RIRB_INT_MASK) {
if (!chip->single_cmd && (status & RIRB_INT_RESPONSE)) if (status & RIRB_INT_RESPONSE)
azx_update_rirb(chip); azx_update_rirb(chip);
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
} }
@ -1098,7 +1132,7 @@ static int azx_setup_periods(struct azx *chip,
pos_align; pos_align;
pos_adj = frames_to_bytes(runtime, pos_adj); pos_adj = frames_to_bytes(runtime, pos_adj);
if (pos_adj >= period_bytes) { 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]); bdl_pos_adj[chip->dev_index]);
pos_adj = 0; pos_adj = 0;
} else { } else {
@ -1122,7 +1156,7 @@ static int azx_setup_periods(struct azx *chip,
return 0; return 0;
error: 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); azx_dev->bufsize, period_bytes);
return -EINVAL; return -EINVAL;
} }
@ -1215,7 +1249,7 @@ static int probe_codec(struct azx *chip, int addr)
chip->probing = 0; chip->probing = 0;
if (res == -1) if (res == -1)
return -EIO; return -EIO;
snd_printdd("hda_intel: codec #%d probed OK\n", addr); snd_printdd(SFX "codec #%d probed OK\n", addr);
return 0; return 0;
} }
@ -1223,6 +1257,26 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *cpcm); struct hda_pcm *cpcm);
static void azx_stop_chip(struct azx *chip); 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 * 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.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response; bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream; bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
bus_temp.ops.bus_reset = azx_bus_reset;
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.power_save = &power_save; bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify; 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 /* Some BIOSen give you wrong codec addresses
* that don't exist * that don't exist
*/ */
snd_printk(KERN_WARNING snd_printk(KERN_WARNING SFX
"hda_intel: Codec #%d probe error; " "Codec #%d probe error; "
"disabling it...\n", c); "disabling it...\n", c);
chip->codec_mask &= ~(1 << c); chip->codec_mask &= ~(1 << c);
/* More badly, accessing to a non-existing /* 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); bufsize = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_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); bufsize, format_val);
if (bufsize != azx_dev->bufsize || if (bufsize != azx_dev->bufsize ||
@ -1830,7 +1885,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
&pcm); &pcm);
if (err < 0) if (err < 0)
return err; return err;
strcpy(pcm->name, cpcm->name); strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
if (apcm == NULL) if (apcm == NULL)
return -ENOMEM; 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++) for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]); snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized) if (chip->initialized)
snd_hda_suspend(chip->bus, state); snd_hda_suspend(chip->bus);
azx_stop_chip(chip); azx_stop_chip(chip);
if (chip->irq >= 0) { if (chip->irq >= 0) {
free_irq(chip->irq, chip); 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); synchronize_irq(chip->irq);
gcap = azx_readw(chip, GCAP); 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 */ /* ATI chips seems buggy about 64bit DMA addresses */
if (chip->driver_type == AZX_DRIVER_ATI) if (chip->driver_type == AZX_DRIVER_ATI)
gcap &= ~0x01; gcap &= ~ICH6_GCAP_64OK;
/* allow 64bit DMA address if supported by H/W */ /* 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)); pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
else { else {
pci_set_dma_mask(pci, DMA_BIT_MASK(32)); 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), chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
GFP_KERNEL); GFP_KERNEL);
if (!chip->azx_dev) { 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; goto errout;
} }
@ -2332,11 +2387,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
goto errout; goto errout;
} }
/* allocate CORB/RIRB */ /* allocate CORB/RIRB */
if (!chip->single_cmd) { err = azx_alloc_cmd_io(chip);
err = azx_alloc_cmd_io(chip); if (err < 0)
if (err < 0) goto errout;
goto errout;
}
/* initialize streams */ /* initialize streams */
azx_init_stream(chip); 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->driver, "HDA-Intel");
strcpy(card->shortname, driver_short_names[chip->driver_type]); strlcpy(card->shortname, driver_short_names[chip->driver_type],
sprintf(card->longname, "%s at 0x%lx irq %i", sizeof(card->shortname));
card->shortname, chip->addr, chip->irq); snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, chip->addr, chip->irq);
*rchip = chip; *rchip = chip;
return 0; return 0;
@ -2514,6 +2569,20 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */ /* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, { 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 */ /* AMD Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .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; hda_nid_t nid;
int i, nodes; int i, nodes;
snd_iprintf(buffer, "Codec: %s\n", snd_iprintf(buffer, "Codec: ");
codec->name ? codec->name : "Not Set"); 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, "Address: %d\n", codec->addr);
snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id); snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_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; 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[] = { 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 */ /* 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 */ {} /* terminator */
}; };
@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec)
* Digital out * Digital out
*/ */
static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec, struct hda_codec *codec,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct nvhdmi_spec *spec = codec->spec; struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout); return snd_hda_multi_out_dig_open(codec, &spec->multiout);
} }
static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec, struct hda_codec *codec,
struct snd_pcm_substream *substream) 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; struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout); return snd_hda_multi_out_dig_close(codec, &spec->multiout);
} }
static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec, struct hda_codec *codec,
unsigned int stream_tag, unsigned int stream_tag,
unsigned int format, unsigned int format,
struct snd_pcm_substream *substream) 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; struct nvhdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, 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, .substreams = 1,
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 8,
.nid = 0x4, /* NID to query formats and rates and setup streams */ .nid = Nv_Master_Convert_nid,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
.maxbps = 16, .maxbps = 16,
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.ops = { .ops = {
.open = nvhdmi_dig_playback_pcm_open, .open = nvhdmi_dig_playback_pcm_open,
.close = nvhdmi_dig_playback_pcm_close, .close = nvhdmi_dig_playback_pcm_close_8ch,
.prepare = nvhdmi_dig_playback_pcm_prepare .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 nvhdmi_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm_rec; 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->name = "NVIDIA HDMI";
info->pcm_type = HDA_PCM_TYPE_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; return 0;
} }
@ -127,14 +320,21 @@ static void nvhdmi_free(struct hda_codec *codec)
kfree(codec->spec); 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_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms, .build_pcms = nvhdmi_build_pcms_8ch,
.init = nvhdmi_init, .init = nvhdmi_init,
.free = nvhdmi_free, .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; struct nvhdmi_spec *spec;
@ -144,13 +344,30 @@ static int patch_nvhdmi(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */ spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2; spec->multiout.max_channels = 8;
spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital, spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
* seems to be unused in pure-digital
* case. */
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; return 0;
} }
@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
* patch entries * patch entries
*/ */
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi }, { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi }, { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi }, { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{} /* terminator */ {} /* terminator */
}; };

File diff suppressed because it is too large Load Diff

View File

@ -100,6 +100,7 @@ enum {
STAC_HP_M4, STAC_HP_M4,
STAC_HP_DV5, STAC_HP_DV5,
STAC_HP_HDX, STAC_HP_HDX,
STAC_HP_DV4_1222NR,
STAC_92HD71BXX_MODELS STAC_92HD71BXX_MODELS
}; };
@ -193,6 +194,7 @@ struct sigmatel_spec {
unsigned int gpio_dir; unsigned int gpio_dir;
unsigned int gpio_data; unsigned int gpio_data;
unsigned int gpio_mute; unsigned int gpio_mute;
unsigned int gpio_led;
/* stream */ /* stream */
unsigned int stream_delay; unsigned int stream_delay;
@ -634,6 +636,40 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
return 0; 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) static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 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), \ .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[] = { static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 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_M4] = NULL,
[STAC_HP_DV5] = NULL, [STAC_HP_DV5] = NULL,
[STAC_HP_HDX] = NULL, [STAC_HP_HDX] = NULL,
[STAC_HP_DV4_1222NR] = NULL,
}; };
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { 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_M4] = "hp-m4",
[STAC_HP_DV5] = "hp-dv5", [STAC_HP_DV5] = "hp-dv5",
[STAC_HP_HDX] = "hp-hdx", [STAC_HP_HDX] = "hp-hdx",
[STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
}; };
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { 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), "DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD71BXX_REF), "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, SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
"HP", STAC_HP_DV5), "HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, 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; 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); unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; 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; 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) 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 hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec; 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; 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 hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = kcontrol->private_value >> 8; hda_nid_t nid = kcontrol->private_value;
int io_idx = kcontrol-> private_value & 0xff; int io_idx = (nid == spec->mic_switch) ? 1 : 0;
unsigned short val = !!ucontrol->value.integer.value[0]; unsigned short val = !!ucontrol->value.enumerated.item[0];
spec->io_switch[io_idx] = val; spec->io_switch[io_idx] = val;
@ -2626,7 +2771,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
else { else {
unsigned int pinctl = AC_PINCTL_IN_EN; unsigned int pinctl = AC_PINCTL_IN_EN;
if (io_idx) /* set VREF for mic */ 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); stac92xx_auto_set_pinctl(codec, nid, pinctl);
} }
@ -2707,7 +2852,8 @@ enum {
STAC_CTL_WIDGET_AMP_VOL, STAC_CTL_WIDGET_AMP_VOL,
STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_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[] = { 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_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0),
DC_BIAS(NULL, 0, 0),
}; };
/* add dynamic controls */ /* add dynamic controls */
@ -2782,6 +2929,34 @@ static struct snd_kcontrol_new stac_input_src_temp = {
.put = stac92xx_mux_enum_put, .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) static int stac92xx_add_input_source(struct sigmatel_spec *spec)
{ {
struct snd_kcontrol_new *knew; 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) const struct auto_pin_cfg *cfg)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid;
int err; int err;
int idx;
err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
spec->multiout.dac_nids, spec->multiout.dac_nids,
@ -3161,20 +3338,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err; return err;
} }
if (spec->line_switch) { for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, nid = cfg->input_pins[idx];
"Line In as Output Switch", if (nid) {
spec->line_switch << 8); err = stac92xx_add_jack_mode_control(codec, nid, idx);
if (err < 0) if (err < 0)
return err; 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;
} }
return 0; 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); err = snd_hda_attach_beep_device(codec, nid);
if (err < 0) if (err < 0)
return err; 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 */ /* if no beep switch is available, make its own one */
caps = query_amp_caps(codec, nid, HDA_OUTPUT); caps = query_amp_caps(codec, nid, HDA_OUTPUT);
if (codec->beep && if (codec->beep &&
@ -4082,7 +4254,7 @@ static int stac92xx_init(struct hda_codec *codec)
unsigned int pinctl, conf; unsigned int pinctl, conf;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
/* for mic pins, force to initialize */ /* for mic pins, force to initialize */
pinctl = stac92xx_get_vref(codec, nid); pinctl = stac92xx_get_default_vref(codec, nid);
pinctl |= AC_PINCTL_IN_EN; pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl); stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else { } else {
@ -4535,17 +4707,19 @@ static int stac92xx_resume(struct hda_codec *codec)
return 0; 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) * check for mute state only on Speakers (nid = 0x10)
* *
* For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
* the LED is NOT working properly ! * 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 #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) hda_nid_t nid)
{ {
struct sigmatel_spec *spec = codec->spec; 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 (nid == 0x10) {
if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
HDA_AMP_MUTE) HDA_AMP_MUTE)
spec->gpio_data &= ~0x08; /* orange */ spec->gpio_data &= ~spec->gpio_led; /* orange */
else else
spec->gpio_data |= 0x08; /* white */ spec->gpio_data |= spec->gpio_led; /* white */
stac_gpio_set(codec, spec->gpio_mask, stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir, spec->gpio_dir,
@ -5201,6 +5375,15 @@ again:
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init); 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_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50; spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0; spec->aloopback_shift = 0;
@ -5234,6 +5417,15 @@ again:
spec->num_smuxes = 0; spec->num_smuxes = 0;
spec->num_dmuxes = 1; spec->num_dmuxes = 1;
break; 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: case STAC_HP_DV5:
snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
@ -5242,22 +5434,21 @@ again:
spec->num_dmics = 1; spec->num_dmics = 1;
spec->num_dmuxes = 1; spec->num_dmuxes = 1;
spec->num_smuxes = 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 */ /* orange/white mute led on GPIO3, orange=0, white=1 */
spec->gpio_mask |= 0x08; spec->gpio_led = 0x08;
spec->gpio_dir |= 0x08; break;
spec->gpio_data |= 0x08; /* set to white */ }
#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. */ /* register check_power_status callback. */
codec->patch_ops.check_power_status = codec->patch_ops.check_power_status =
stac92xx_hp_hdx_check_power_status; stac92xx_hp_check_power_status;
}
#endif #endif
break;
};
spec->multiout.dac_nids = spec->dac_nids; spec->multiout.dac_nids = spec->dac_nids;
if (spec->dinput_mux) if (spec->dinput_mux)
@ -5282,7 +5473,7 @@ again:
codec->proc_widget_hook = stac92hd7x_proc_hook; codec->proc_widget_hook = stac92hd7x_proc_hook;
return 0; return 0;
}; }
static int patch_stac922x(struct hda_codec *codec) 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 */ /* correct the device field to SPDIF out */
snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
break; break;
}; }
/* configure the analog microphone on some laptops */ /* configure the analog microphone on some laptops */
snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */ /* correct the front output jack as a hp out */

View File

@ -205,7 +205,7 @@ struct via_spec {
/* playback */ /* playback */
struct hda_multi_out multiout; struct hda_multi_out multiout;
hda_nid_t extra_dig_out_nid; hda_nid_t slave_dig_outs[2];
/* capture */ /* capture */
unsigned int num_adc_nids; 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); 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, static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec, struct hda_codec *codec,
unsigned int stream_tag, 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 snd_pcm_substream *substream)
{ {
struct via_spec *spec = codec->spec; 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 */ static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
if (substream->number == 0) struct hda_codec *codec,
nid = spec->multiout.dig_out_nid; struct snd_pcm_substream *substream)
else if (substream->number == 1) {
nid = spec->extra_dig_out_nid; struct via_spec *spec = codec->spec;
else snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
return -1;
mutex_lock(&codec->spdif_mutex);
setup_dig_playback_stream(codec, nid, stream_tag, format);
mutex_unlock(&codec->spdif_mutex);
return 0; return 0;
} }
@ -842,7 +824,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = {
.ops = { .ops = {
.open = via_dig_playback_pcm_open, .open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close, .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) if (err < 0)
return err; return err;
spec->multiout.share_spdif = 1; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, 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); via_gpio_control(codec);
} }
static hda_nid_t slave_dig_outs[] = {
0,
};
static int via_init(struct hda_codec *codec) static int via_init(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; 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, snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
/* no slave outs */ /* assign slave outs */
codec->slave_dig_outs = slave_dig_outs; if (spec->slave_dig_outs[0])
codec->slave_dig_outs = spec->slave_dig_outs;
return 0; return 0;
} }
@ -2134,7 +2107,8 @@ static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
.ops = { .ops = {
.open = via_dig_playback_pcm_open, .open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close, .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 = { static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
.substreams = 2, .substreams = 1,
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
/* NID is set in via_build_pcms */ /* NID is set in via_build_pcms */
.ops = { .ops = {
.open = via_dig_playback_pcm_open, .open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close, .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; 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) static int vt1708S_parse_auto_config(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
int err; int err;
static hda_nid_t vt1708s_ignore[] = {0x21, 0};
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
vt1708s_ignore);
if (err < 0) if (err < 0)
return err; return err;
err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 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; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_outs) fill_dig_outs(codec);
spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
spec->extra_dig_out_nid = 0x15;
if (spec->kctls.list) if (spec->kctls.list)
spec->mixers[spec->num_mixers++] = 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 = { .ops = {
.open = via_dig_playback_pcm_open, .open = via_dig_playback_pcm_open,
.close = via_dig_playback_pcm_close, .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; struct via_spec *spec = codec->spec;
int err; int err;
static hda_nid_t vt1702_ignore[] = {0x1C, 0};
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
vt1702_ignore);
if (err < 0) if (err < 0)
return err; return err;
err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 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; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->autocfg.dig_outs) fill_dig_outs(codec);
spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
spec->extra_dig_out_nid = 0x1B;
if (spec->kctls.list) if (spec->kctls.list)
spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->mixers[spec->num_mixers++] = spec->kctls.list;