UpstreamStatus: Merged upstream
This patch finalizes the changes from upstream CVS adding
audible
functionality for a variety of HDA chipsets not reliant on
individual
patch_*.c codecs. The important fixes include falling back
to a single
mode when autoprobing fails (preventing azx_get_response
floods),
adding helper routines to reenable sound on select Fujitsu
laptops, and
fixing PM support on HDA chipsets.
Signed-off-by: Daniel T Chen <crimsun ubuntu.com>
---
sound/pci/hda/hda_codec.c | 271
+++++++++++++++++++++++++++++++++++--------
sound/pci/hda/hda_codec.h | 18 ++-
sound/pci/hda/hda_generic.c | 23 +++-
sound/pci/hda/hda_intel.c | 169
+++++++++++++++------------
sound/pci/hda/hda_local.h | 56 ++++++++-
sound/pci/hda/hda_proc.c | 1
6 files changed, 403 insertions(+), 135 deletions(-)
a5f42db12d572b3aae9fbdb0c65e8a06ed5de86c
diff --git a/sound/pci/hda/hda_codec.c
b/sound/pci/hda/hda_codec.c
index 0dbeeaf..a537397 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
 -155,8
+155,9  int snd_hda_get_connections(struct hda_c
hda_nid_t *conn_list, int max_conns)
{
unsigned int parm;
- int i, j, conn_len, num_tupples, conns;
+ int i, conn_len, conns;
unsigned int shift, num_elems, mask;
+ hda_nid_t prev_nid;
snd_assert(conn_list && max_conns > 0, return
-EINVAL);
 -171,7
+172,6  int snd_hda_get_connections(struct hda_c
num_elems = 4;
}
conn_len = parm & AC_CLIST_LENGTH;
- num_tupples = num_elems / 2;
mask = (1 << (shift-1)) - 1;
if (! conn_len)
 -186,40
+186,38  int snd_hda_get_connections(struct hda_c
/* multi connection */
conns = 0;
- for (i = 0; i < conn_len; i += num_elems) {
- parm = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_LIST, i);
- for (j = 0; j < num_tupples; j++) {
- int range_val;
- hda_nid_t val1, val2, n;
- range_val = parm & (1 << (shift-1)); /* ranges
*/
- val1 = parm & mask;
- parm >>= shift;
- val2 = parm & mask;
- parm >>= shift;
- if (range_val) {
- /* ranges between val1 and val2 */
- if (val1 > val2) {
- snd_printk(KERN_WARNING "hda_codec: invalid
dep_range_val %x:%x\n", val1, val2);
- continue;
- }
- for (n = val1; n <= val2; n++) {
- if (conns >= max_conns)
- return -EINVAL;
- conn_list[conns++] = n;
- }
- } else {
- if (! val1)
- break;
- if (conns >= max_conns)
- return -EINVAL;
- conn_list[conns++] = val1;
- if (! val2)
- break;
- if (conns >= max_conns)
+ prev_nid = 0;
+ for (i = 0; i < conn_len; i++) {
+ int range_val;
+ hda_nid_t val, n;
+
+ if (i % num_elems == 0)
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_LIST, i);
+ range_val = !! (parm & (1 << (shift-1))); /*
ranges */
+ val = parm & mask;
+ parm >>= shift;
+ if (range_val) {
+ /* ranges between the previous and this one */
+ if (! prev_nid || prev_nid >= val) {
+ snd_printk(KERN_WARNING "hda_codec: invalid
dep_range_val %x:%x\n", prev_nid, val);
+ continue;
+ }
+ for (n = prev_nid + 1; n <= val; n++) {
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "Too many
connections\n");
return -EINVAL;
- conn_list[conns++] = val2;
+ }
+ conn_list[conns++] = n;
}
+ } else {
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "Too many
connections\n");
+ return -EINVAL;
+ }
+ conn_list[conns++] = val;
}
+ prev_nid = val;
}
return conns;
}
 -288,6
+286,9  static int init_unsol_queue(struct hda_b
{
struct hda_bus_unsolicited *unsol;
+ if (bus->unsol) /* already initialized */
+ return 0;
+
unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
if (! unsol) {
snd_printk(KERN_ERR "hda_codec: can't allocate
unsolicited queue\n");
 -373,8
+374,6  int snd_hda_bus_new(snd_card_t *card, co
init_MUTEX(&bus->cmd_mutex);
INIT_LIST_HEAD(&bus->codec_list);
- init_unsol_queue(bus);
-
if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus,
&dev_ops)) < 0) {
snd_hda_bus_free(bus);
return err;
 -455,6
+454,27  static void setup_fg_nodes(struct hda_co
}
/*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec,
hda_nid_t fg_node)
+{
+ int i;
+ hda_nid_t nid;
+
+ codec->num_nodes = snd_hda_get_sub_nodes(codec,
fg_node,
+ &codec->start_nid);
+ codec->wcaps = kmalloc(codec->num_nodes * 4,
GFP_KERNEL);
+ if (! codec->wcaps)
+ return -ENOMEM;
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hda_param_read(codec, nid,
+ AC_PAR_AUDIO_WIDGET_CAP);
+ return 0;
+}
+
+
+/*
* codec destructor
*/
static void snd_hda_codec_free(struct hda_codec *codec)
 -465,6
+485,8  static void snd_hda_codec_free(struct hd
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ kfree(codec->amp_info);
+ kfree(codec->wcaps);
kfree(codec);
}
 -508,6
+530,12  int snd_hda_codec_new(struct hda_bus *bu
bus->caddr_tbl[codec_addr] = codec;
codec->vendor_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+ if (codec->vendor_id == -1)
+ /* read again, hopefully the access method was corrected
+ * in the last read...
+ */
+ codec->vendor_id = snd_hda_param_read(codec,
AC_NODE_ROOT,
+ AC_PAR_VENDOR_ID);
codec->subsystem_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
codec->revision_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_REV_ID);
 -518,6
+546,12  int snd_hda_codec_new(struct hda_bus *bu
return -ENODEV;
}
+ if (read_widget_caps(codec, codec->afg ? codec->afg
: codec->mfg) < 0) {
+ snd_printk(KERN_ERR "hda_codec: cannot
malloc\n");
+ snd_hda_codec_free(codec);
+ return -ENOMEM;
+ }
+
if (! codec->subsystem_id) {
hda_nid_t nid = codec->afg ? codec->afg :
codec->mfg;
codec->subsystem_id = snd_hda_codec_read(codec, nid,
0,
 -539,6
+573,9  int snd_hda_codec_new(struct hda_bus *bu
return err;
}
+ if (codec->patch_ops.unsol_event)
+ init_unsol_queue(bus);
+
snd_hda_codec_proc_new(codec);
sprintf(component, "HDA:%08x",
codec->vendor_id);
 -586,6
+623,8  static void init_amp_hash(struct hda_cod
{
memset(codec->amp_hash, 0xff,
sizeof(codec->amp_hash));
codec->num_amp_entries = 0;
+ codec->amp_info_size = 0;
+ codec->amp_info = NULL;
}
/* query the hash. allocate an entry if not found. */
 -603,9
+642,22  static struct hda_amp_info *get_alloc_am
}
/* add a new hash entry */
- if (codec->num_amp_entries >=
ARRAY_SIZE(codec->amp_info)) {
- snd_printk(KERN_ERR "hda_codec: Tooooo many
amps!\n");
- return NULL;
+ if (codec->num_amp_entries >=
codec->amp_info_size) {
+ /* reallocate the array */
+ int new_size = codec->amp_info_size + 64;
+ struct hda_amp_info *new_info = kcalloc(new_size,
sizeof(struct hda_amp_info),
+ GFP_KERNEL);
+ if (! new_info) {
+ snd_printk(KERN_ERR "hda_codec: can't malloc
amp_info\n");
+ return NULL;
+ }
+ if (codec->amp_info) {
+ memcpy(new_info, codec->amp_info,
+ codec->amp_info_size * sizeof(struct
hda_amp_info));
+ kfree(codec->amp_info);
+ }
+ codec->amp_info_size = new_size;
+ codec->amp_info = new_info;
}
cur = codec->num_amp_entries++;
info = &codec->amp_info[cur];
 -627,7
+679,7  static u32 query_amp_caps(struct hda_cod
if (! info)
return 0;
if (! (info->status & INFO_AMP_CAPS)) {
- if (!(snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD))
+ if (! (get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
nid = codec->afg;
info->amp_caps = snd_hda_param_read(codec, nid,
direction == HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
 -676,7
+728,8  static void put_vol_mute(struct hda_code
/*
* read AMP value. The volume is between 0 to 0x7f, 0x80 =
mute bit.
*/
-static int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int index)
+int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch,
+ int direction, int index)
{
struct hda_amp_info *info = get_alloc_amp_hash(codec,
HDA_HASH_KEY(nid, direction, index));
if (! info)
 -687,7
+740,8  static int snd_hda_codec_amp_read(struct
/*
* update the AMP value, mask = bit mask to set, val = the
value
*/
-static int snd_hda_codec_amp_update(struct hda_codec
*codec, hda_nid_t nid, int ch, int direction, int idx, int
mask, int val)
+int snd_hda_codec_amp_update(struct hda_codec *codec,
hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val)
{
struct hda_amp_info *info = get_alloc_amp_hash(codec,
HDA_HASH_KEY(nid, direction, idx));
 -1175,6
+1229,31  int snd_hda_create_spdif_in_ctls(struct
}
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec,
hda_nid_t fg,
+ unsigned int power_state)
+{
+ hda_nid_t nid, nid_start;
+ int nodes;
+
+ snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ power_state);
+
+ nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
+ for (nid = nid_start; nid < nodes + nid_start; nid++) {
+ if (get_wcaps(codec, nid) & AC_WCAP_POWER)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ power_state);
+ }
+
+ if (power_state == AC_PWRST_D0)
+ msleep(10);
+}
+
+
/**
* snd_hda_build_controls - build mixer controls
* bus: the BUS
 -1202,6
+1281,9  int snd_hda_build_controls(struct hda_bu
list_for_each(p, &bus->codec_list) {
struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
int err;
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
if (! codec->patch_ops.init)
continue;
err = codec->patch_ops.init(codec);
 -1320,7
+1402,7  int snd_hda_query_supported_pcm(struct h
val = 0;
if (nid != codec->afg &&
- snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return -EIO;
 -1342,7
+1424,7  int snd_hda_query_supported_pcm(struct h
unsigned int bps;
unsigned int wcaps;
- wcaps = snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+ wcaps = get_wcaps(codec, nid);
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (streams == -1)
return -EIO;
 -1412,7
+1494,7  int snd_hda_is_supported_format(struct h
unsigned int val = 0, rate, stream;
if (nid != codec->afg &&
- snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return 0;
 -1638,14
+1720,74  int snd_hda_add_new_ctls(struct hda_code
int err;
for (; knew->name; knew++) {
- err = snd_ctl_add(codec->bus->card,
snd_ctl_new1(knew, codec));
- if (err < 0)
- return err;
+ snd_kcontrol_t *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (! kctl)
+ return -ENOMEM;
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0) {
+ if (! codec->addr)
+ return err;
+ kctl = snd_ctl_new1(knew, codec);
+ if (! kctl)
+ return -ENOMEM;
+ kctl->id.device = codec->addr;
+ if ((err = snd_ctl_add(codec->bus->card, kctl))
< 0)
+ return err;
+ }
}
return 0;
}
+ /*
+ * Channel mode helper
+ */
+int snd_hda_ch_mode_info(struct hda_codec *codec,
snd_ctl_elem_info_t *uinfo,
+ const struct hda_channel_mode *chmode, int num_chmodes)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_chmodes;
+ if (uinfo->value.enumerated.item >= num_chmodes)
+ uinfo->value.enumerated.item = num_chmodes - 1;
+ sprintf(uinfo->value.enumerated.name,
"%dch",
+ chmode[uinfo->value.enumerated.item].channels);
+ return 0;
+}
+
+int snd_hda_ch_mode_get(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int max_channels)
+{
+ int i;
+
+ for (i = 0; i < num_chmodes; i++) {
+ if (max_channels == chmode[i].channels) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
+ }
+ return 0;
+}
+
+int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int *max_channelsp)
+{
+ unsigned int mode;
+
+ mode = ucontrol->value.enumerated.item[0];
+ snd_assert(mode < num_chmodes, return -EINVAL);
+ if (*max_channelsp == chmode[mode].channels && !
codec->in_resume)
+ return 0;
+ /* change the current channel setting */
+ *max_channelsp = chmode[mode].channels;
+ if (chmode[mode].sequence)
+ snd_hda_sequence_write(codec, chmode[mode].sequence);
+ return 1;
+}
+
/*
* input MUX helper
*/
 -1792,8
+1934,18  int snd_hda_multi_out_analog_cleanup(str
/*
* Helper for automatic ping configuration
*/
+
+static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
/* parse all pin widgets and store the useful pin nids to
cfg */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg)
+int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
+ hda_nid_t *ignore_nids)
{
hda_nid_t nid, nid_start;
int i, j, nodes;
 -1806,8
+1958,7  int snd_hda_parse_pin_def_config(struct
nodes = snd_hda_get_sub_nodes(codec, codec->afg,
&nid_start);
for (nid = nid_start; nid < nodes + nid_start; nid++) {
- unsigned int wid_caps = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type = (wid_caps & AC_WCAP_TYPE)
>> AC_WCAP_TYPE_SHIFT;
unsigned int def_conf;
short assoc, loc;
 -1815,13
+1966,16  int snd_hda_parse_pin_def_config(struct
/* read all default configuration for pin complex */
if (wid_type != AC_WID_PIN)
continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid,
ignore_nids))
+ continue;
+
def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
switch (get_defcfg_device(def_conf)) {
case AC_JACK_LINE_OUT:
- case AC_JACK_SPEAKER:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
if (! assoc)
 -1836,6
+1990,9  int snd_hda_parse_pin_def_config(struct
sequences[cfg->line_outs] = seq;
cfg->line_outs++;
break;
+ case AC_JACK_SPEAKER:
+ cfg->speaker_pin = nid;
+ break;
case AC_JACK_HP_OUT:
cfg->hp_pin = nid;
break;
 -1902,6
+2059,12  int snd_hda_parse_pin_def_config(struct
return 0;
}
+/* labels for input pins */
+const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
+ "Mic", "Front Mic",
"Line", "Front Line",
"CD", "Aux"
+};
+
+
#ifdef CONFIG_PM
/*
* power management
 -1923,6
+2086,9  int snd_hda_suspend(struct hda_bus *bus,
struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec, state);
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D3);
}
return 0;
}
 -1940,6
+2106,9  int snd_hda_resume(struct hda_bus *bus)
list_for_each(p, &bus->codec_list) {
struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
}
diff --git a/sound/pci/hda/hda_codec.h
b/sound/pci/hda/hda_codec.h
index 1179d6c..fc5cbfe 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
 -98,7
+98,7  enum {
#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
#define AC_VERB_SET_PIN_SENSE 0x709
#define AC_VERB_SET_BEEP_CONTROL 0x70a
-#define AC_VERB_SET_EAPD_BTLENALBE 0x70c
+#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
 -214,6
+214,12  enum {
#define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3)
+/* Power state values */
+#define AC_PWRST_D0 0x00
+#define AC_PWRST_D1 0x01
+#define AC_PWRST_D2 0x02
+#define AC_PWRST_D3 0x03
+
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
#define AC_PCAP_NUM_COEF (0xff<<8)
 -376,7
+382,7  enum {
};
/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 16
+#define HDA_MAX_CONNECTIONS 32
/* max. codec address */
#define HDA_MAX_CODEC_ADDRESS 0x0f
 -542,10
+548,16  struct hda_codec {
/* codec specific info */
void *spec;
+ /* widget capabilities cache */
+ unsigned int num_nodes;
+ hda_nid_t start_nid;
+ u32 *wcaps;
+
/* hash for amp access */
u16 amp_hash[32];
int num_amp_entries;
- struct hda_amp_info amp_info[128]; /* big enough? */
+ int amp_info_size;
+ struct hda_amp_info *amp_info;
struct semaphore spdif_mutex;
unsigned int spdif_status; /* IEC958 status bits */
diff --git a/sound/pci/hda/hda_generic.c
b/sound/pci/hda/hda_generic.c
index d0eb9f2..00ead66 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
 -32,7
+32,8 
struct hda_gnode {
hda_nid_t nid; /* NID of this widget */
unsigned short nconns; /* number of input connections */
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* input
connections */
+ hda_nid_t *conn_list;
+ hda_nid_t slist[2]; /* temporay list */
unsigned int wid_caps; /* widget capabilities */
unsigned char type; /* widget type */
unsigned char pin_ctl; /* pin controls */
 -84,6
+85,8  static void snd_hda_generic_free(struct
/* free all widgets */
list_for_each_safe(p, n, &spec->nid_list) {
struct hda_gnode *node = list_entry(p, struct hda_gnode,
list);
+ if (node->conn_list != node->slist)
+ kfree(node->conn_list);
kfree(node);
}
kfree(spec);
 -97,18
+100,32  static int add_new_node(struct hda_codec
{
struct hda_gnode *node;
int nconns;
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
return -ENOMEM;
node->nid = nid;
- nconns = snd_hda_get_connections(codec, nid,
node->conn_list, HDA_MAX_CONNECTIONS);
+ nconns = snd_hda_get_connections(codec, nid, conn_list,
+ HDA_MAX_CONNECTIONS);
if (nconns < 0) {
kfree(node);
return nconns;
}
+ if (nconns <= ARRAY_SIZE(node->slist))
+ node->conn_list = node->slist;
+ else {
+ node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
+ GFP_KERNEL);
+ if (! node->conn_list) {
+ snd_printk(KERN_ERR "hda-generic: cannot
malloc\n");
+ kfree(node);
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->conn_list, conn_list, nconns);
node->nconns = nconns;
- node->wid_caps = snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+ node->wid_caps = get_wcaps(codec, nid);
node->type = (node->wid_caps & AC_WCAP_TYPE)
>> AC_WCAP_TYPE_SHIFT;
if (node->type == AC_WID_PIN) {
diff --git a/sound/pci/hda/hda_intel.c
b/sound/pci/hda/hda_intel.c
index 646b23f..93f4cff 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
 -37,6
+37,7 
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
 -51,6
+52,8  static int index = SNDRV_DEFAULT_IDX1;
static char *id = SNDRV_DEFAULT_STR1;
static char *model;
static int position_fix;
+static int probe_mask = -1;
+static int single_cmd;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD
audio interface.");
 -60,6
+63,11  module_param(model, charp, 0444);
MODULE_PARM_DESC(model, "Use the given board
model.");
module_param(position_fix, int, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 =
auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+module_param(probe_mask, int, 0444);
+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs
(default = -1).");
+module_param(single_cmd, bool, 0444);
+MODULE_PARM_DESC(single_cmd, "Use single command to
communicate with codecs (for debugging only).");
+
/* just for backward compatibility */
static int enable;
 -230,12
+238,6  enum {
#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
/*
- * Use CORB/RIRB for communication from/to codecs.
- * This is the way recommended by Intel (see below).
- */
-#define USE_CORB_RIRB
-
-/*
*/
typedef struct snd_azx azx_t;
 -251,7
+253,6  struct snd_azx_dev {
unsigned int fragsize; /* size of each period in bytes */
unsigned int frags; /* number for period in the play
buffer */
unsigned int fifo_size; /* FIFO size */
- unsigned int last_pos; /* last updated period position */
void __iomem *sd_addr; /* stream descriptor pointer */
 -262,10
+263,11  struct snd_azx_dev {
unsigned int format_val; /* format value to be set in the
controller and the codec */
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
+ /* for sanity check of position buffer */
+ unsigned int period_intr;
unsigned int opened: 1;
unsigned int running: 1;
- unsigned int period_updating: 1;
};
/* CORB/RIRB */
 -324,6
+326,7  struct snd_azx {
/* flags */
int position_fix;
unsigned int initialized: 1;
+ unsigned int single_cmd: 1;
};
/* driver types */
 -387,7
+390,6  static char *driver_short_names[] __devi
* Interface for HD codec
*/
-#ifdef USE_CORB_RIRB
/*
* CORB / RIRB interface
*/
 -435,11
+437,7  static void azx_init_cmd_io(azx_t *chip)
/* set N=1, get RIRB response interrupt for new entry */
azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */
-#ifdef USE_CORB_RIRB
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN |
ICH6_RBCTL_IRQ_EN);
-#else
- azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN);
-#endif
chip->rirb.rp = chip->rirb.cmds = 0;
}
 -451,8
+449,8  static void azx_free_cmd_io(azx_t *chip)
}
/* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid, int direct,
- unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec,
hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int para)
{
azx_t *chip = codec->bus->private_data;
unsigned int wp;
 -508,16
+506,21  static void azx_update_rirb(azx_t *chip)
}
/* receive a response */
-static unsigned int azx_get_response(struct hda_codec
*codec)
+static unsigned int azx_rirb_get_response(struct hda_codec
*codec)
{
azx_t *chip = codec->bus->private_data;
int timeout = 50;
while (chip->rirb.cmds) {
if (! --timeout) {
- snd_printk(KERN_ERR "azx_get_response
timeout\n");
+ snd_printk(KERN_ERR
+ "hda_intel: azx_get_response timeout, "
+ "switching to single_cmd mode...\n");
chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0;
+ /* switch to single_cmd mode */
+ chip->single_cmd = 1;
+ azx_free_cmd_io(chip);
return -1;
}
msleep(1);
 -525,7
+528,6  static unsigned int azx_get_response(str
return chip->rirb.res; /* the last value */
}
-#else
/*
* Use the single immediate command instead of CORB/RIRB
for simplicity
*
 -536,13
+538,10  static unsigned int azx_get_response(str
* I left the codes, however, for debugging/testing
purposes.
*/
-#define azx_alloc_cmd_io(chip) 0
-#define azx_init_cmd_io(chip)
-#define azx_free_cmd_io(chip)
-
/* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid, int direct,
- unsigned int verb, unsigned int para)
+static int azx_single_send_cmd(struct hda_codec *codec,
hda_nid_t nid,
+ int direct, unsigned int verb,
+ unsigned int para)
{
azx_t *chip = codec->bus->private_data;
u32 val;
 -570,7
+569,7  static int azx_send_cmd(struct hda_codec
}
/* receive a response */
-static unsigned int azx_get_response(struct hda_codec
*codec)
+static unsigned int azx_single_get_response(struct
hda_codec *codec)
{
azx_t *chip = codec->bus->private_data;
int timeout = 50;
 -585,9
+584,35  static unsigned int azx_get_response(str
return (unsigned int)-1;
}
-#define azx_update_rirb(chip)
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks
according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid,
+ int direct, unsigned int verb,
+ unsigned int para)
+{
+ struct azx *chip = codec->bus->private_data;
+ if (chip->single_cmd)
+ return azx_single_send_cmd(codec, nid, direct, verb,
para);
+ else
+ return azx_corb_send_cmd(codec, nid, direct, verb, para);
+}
+
+/* get a response */
+static unsigned int azx_get_response(struct hda_codec
*codec)
+{
+ struct azx *chip = codec->bus->private_data;
+ if (chip->single_cmd)
+ return azx_single_get_response(codec);
+ else
+ return azx_rirb_get_response(codec);
+}
-#endif /* USE_CORB_RIRB */
/* reset codec link */
static int azx_reset(azx_t *chip)
 -734,7
+759,8  static void azx_init_chip(azx_t *chip)
azx_int_enable(chip);
/* initialize the codec command I/O */
- azx_init_cmd_io(chip);
+ if (! chip->single_cmd)
+ azx_init_cmd_io(chip);
/* program the position buffer */
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
 -781,11
+807,10  static irqreturn_t azx_interrupt(int irq
if (status & azx_dev->sd_int_sta_mask) {
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
if (azx_dev->substream &&
azx_dev->running) {
- azx_dev->period_updating = 1;
+ azx_dev->period_intr++;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock);
- azx_dev->period_updating = 0;
}
}
}
 -793,7
+818,7  static irqreturn_t azx_interrupt(int irq
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE)
+ if (! chip->single_cmd && (status &
RIRB_INT_RESPONSE))
azx_update_rirb(chip);
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
 -918,7
+943,7  static int __devinit azx_codec_create(az
codecs = 0;
for (c = 0; c < AZX_MAX_CODECS; c++) {
- if (chip->codec_mask & (1 << c)) {
+ if ((chip->codec_mask & (1 << c)) &
probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
continue;
 -1096,7
+1121,6  static int azx_pcm_prepare(snd_pcm_subst
azx_dev->fifo_size = azx_sd_readw(azx_dev,
SD_FIFOSIZE) + 1;
else
azx_dev->fifo_size = 0;
- azx_dev->last_pos = 0;
return hinfo->ops.prepare(hinfo, apcm->codec,
azx_dev->stream_tag,
azx_dev->format_val, substream);
 -1144,39
+1168,24  static snd_pcm_uframes_t azx_pcm_pointer
azx_dev_t *azx_dev = get_azx_dev(substream);
unsigned int pos;
- if (chip->position_fix == POS_FIX_POSBUF) {
+ if (chip->position_fix == POS_FIX_POSBUF ||
+ chip->position_fix == POS_FIX_AUTO) {
/* use the position buffer */
pos = *azx_dev->posbuf;
+ if (chip->position_fix == POS_FIX_AUTO &&
+ azx_dev->period_intr == 1 && ! pos) {
+ printk(KERN_WARNING
+ "hda-intel: Invalid position buffer,
"
+ "using LPIB read method
instead.\n");
+ chip->position_fix = POS_FIX_NONE;
+ goto read_lpib;
+ }
} else {
+ read_lpib:
/* read LPIB */
pos = azx_sd_readl(azx_dev, SD_LPIB);
if (chip->position_fix == POS_FIX_FIFO)
pos += azx_dev->fifo_size;
-#if 0 /* disabled temprarily, auto-correction doesn't work
well... */
- else if (chip->position_fix == POS_FIX_AUTO &&
azx_dev->period_updating) {
- /* check the validity of DMA position */
- unsigned int diff = 0;
- azx_dev->last_pos += azx_dev->fragsize;
- if (azx_dev->last_pos > pos)
- diff = azx_dev->last_pos - pos;
- if (azx_dev->last_pos >= azx_dev->bufsize) {
- if (pos < azx_dev->fragsize)
- diff = 0;
- azx_dev->last_pos = 0;
- }
- if (diff > 0 && diff <=
azx_dev->fifo_size)
- pos += azx_dev->fifo_size;
- else {
- snd_printdd(KERN_INFO "hda_intel: DMA position
fix %d, switching to posbuf\n", diff);
- chip->position_fix = POS_FIX_POSBUF;
- pos = *azx_dev->posbuf;
- }
- azx_dev->period_updating = 0;
- }
-#else
- else if (chip->position_fix == POS_FIX_AUTO)
- pos += azx_dev->fifo_size;
-#endif
}
if (pos >= azx_dev->bufsize)
pos = 0;
 -1328,28
+1337,33  static int __devinit azx_init_stream(azx
/*
* power management
*/
-static int azx_suspend(snd_card_t *card, pm_message_t
state)
+static int azx_suspend(struct pci_dev *pci, pm_message_t
state)
{
- azx_t *chip = card->pm_private_data;
+ snd_card_t *card = pci_get_drvdata(pci);
+ azx_t *chip = card->private_data;
int i;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
for (i = 0; i < chip->pcm_devs; i++)
- if (chip->pcm[i])
- snd_pcm_suspend_all(chip->pcm[i]);
+ snd_pcm_suspend_all(chip->pcm[i]);
snd_hda_suspend(chip->bus, state);
azx_free_cmd_io(chip);
- pci_disable_device(chip->pci);
+ pci_disable_device(pci);
+ pci_save_state(pci);
return 0;
}
-static int azx_resume(snd_card_t *card)
+static int azx_resume(struct pci_dev *pci)
{
- azx_t *chip = card->pm_private_data;
+ snd_card_t *card = pci_get_drvdata(pci);
+ azx_t *chip = card->private_data;
- pci_enable_device(chip->pci);
- pci_set_master(chip->pci);
+ pci_restore_state(pci);
+ pci_enable_device(pci);
+ pci_set_master(pci);
azx_init_chip(chip);
snd_hda_resume(chip->bus);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif /* CONFIG_PM */
 -1409,7
+1423,7  static int azx_dev_free(snd_device_t *de
* constructor
*/
static int __devinit azx_create(snd_card_t *card, struct
pci_dev *pci,
- int posfix, int driver_type,
+ int driver_type,
azx_t **rchip)
{
azx_t *chip;
 -1438,7
+1452,8  static int __devinit azx_create(snd_card
chip->irq = -1;
chip->driver_type = driver_type;
- chip->position_fix = posfix;
+ chip->position_fix = position_fix;
+ chip->single_cmd = single_cmd;
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
 -1509,8
+1524,9  static int __devinit azx_create(snd_card
goto errout;
}
/* allocate CORB/RIRB */
- if ((err = azx_alloc_cmd_io(chip)) < 0)
- goto errout;
+ if (! chip->single_cmd)
+ if ((err = azx_alloc_cmd_io(chip)) < 0)
+ goto errout;
/* initialize streams */
azx_init_stream(chip);
 -1556,11
+1572,12  static int __devinit azx_probe(struct pc
return -ENOMEM;
}
- if ((err = azx_create(card, pci, position_fix,
pci_id->driver_data,
+ if ((err = azx_create(card, pci, pci_id->driver_data,
&chip)) < 0) {
snd_card_free(card);
return err;
}
+ card->private_data = chip;
/* create codec instances */
if ((err = azx_codec_create(chip, model)) < 0) {
 -1580,7
+1597,6  static int __devinit azx_probe(struct pc
return err;
}
- snd_card_set_pm_callback(card, azx_suspend, azx_resume,
chip);
snd_card_set_dev(card, &pci->dev);
if ((err = snd_card_register(card)) < 0) {
 -1621,7
+1637,10  static struct pci_driver driver = {
.id_table = azx_ids,
.probe = azx_probe,
.remove = __devexit_p(azx_remove),
- SND_PCI_PM_CALLBACKS
+#ifdef CONFIG_PM
+ .suspend = azx_suspend,
+ .resume = azx_resume,
+#endif
};
static int __init alsa_card_azx_init(void)
diff --git a/sound/pci/hda/hda_local.h
b/sound/pci/hda/hda_local.h
index f51a56f..5597cd4 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
 -66,6
+66,9  int snd_hda_mixer_amp_volume_put(snd_kco
int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol,
snd_ctl_elem_info_t *uinfo);
int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol);
int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol);
+/* lowlevel accessor with caching; use carefully */
+int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int index);
+int snd_hda_codec_amp_update(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int idx, int mask, int
val);
/* mono switch binding multiple inputs */
#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices,
direction) \
 -87,7
+90,7  int snd_hda_create_spdif_in_ctls(struct
/*
* input MUX helper
*/
-#define HDA_MAX_NUM_INPUTS 8
+#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
const char *label;
unsigned int index;
 -102,6
+105,23  int snd_hda_input_mux_put(struct hda_cod
snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
+ /*
+ * Channel mode helper
+ */
+struct hda_channel_mode {
+ int channels;
+ const struct hda_verb *sequence;
+};
+
+int snd_hda_ch_mode_info(struct hda_codec *codec,
snd_ctl_elem_info_t *uinfo,
+ const struct hda_channel_mode *chmode, int
num_chmodes);
+int snd_hda_ch_mode_get(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int max_channels);
+int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int *max_channelsp);
+
/*
* Multi-channel / digital-out PCM helper
*/
 -194,9
+214,12  enum ;
+extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
+
struct auto_pin_cfg {
int line_outs;
- hda_nid_t line_out_pins[4]; /* sorted in the order of
Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[5]; /* sorted in the order of
Front/Surr/CLFE/Side */
+ hda_nid_t speaker_pin;
hda_nid_t hp_pin;
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin;
 -209,6
+232,33  struct auto_pin_cfg {
#define get_defcfg_sequence(cfg) (cfg &
AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) ((cfg &
AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg);
+int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
+ hda_nid_t *ignore_nids);
+
+/* amp values */
+#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO 0xb000
+/* pinctl values */
+#define PIN_IN 0x20
+#define PIN_VREF80 0x24
+#define PIN_VREF50 0x21
+#define PIN_OUT 0x40
+#define PIN_HP 0xc0
+#define PIN_HP_AMP 0x80
+
+/*
+ * get widget capabilities
+ */
+static inline u32 get_wcaps(struct hda_codec *codec,
hda_nid_t nid)
+{
+ if (nid < codec->start_nid ||
+ nid >= codec->start_nid + codec->num_nodes)
+ return snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+ return codec->wcaps[nid - codec->start_nid];
+}
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c
b/sound/pci/hda/hda_proc.c
index 39ddf1c..d5693e6 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
 -26,6
+26,7 
#include <linux/pci.h>
#include <sound/core.h>
#include "hda_codec.h"
+#include "hda_local.h"
static const char *get_wid_type_name(unsigned int
wid_value)
{
--
1.1.3
--
Daniel T. Chen crimsun ubuntu.com
GPG key: www.sh.nu/~crimsun/pubkey.gpg.asc
--
kernel-team mailing list
kernel-team lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team
|