Merge branch 'topic/hda-gen-parser' into for-next
This is a merge of really big changes: the generic parser is heavily enhanced for handling all cases, based on the former Realtek codec driver code. And all codec drivers except for a few ones (CA0132, HDMI and modem) have been converted to use the new generic driver. Conflicts: sound/pci/hda/patch_realtek.c
This commit is contained in:
@@ -86,6 +86,7 @@ config SND_HDA_PATCH_LOADER
|
||||
config SND_HDA_CODEC_REALTEK
|
||||
bool "Build Realtek HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include Realtek HD-audio codec support in
|
||||
snd-hda-intel driver, such as ALC880.
|
||||
@@ -98,6 +99,7 @@ config SND_HDA_CODEC_REALTEK
|
||||
config SND_HDA_CODEC_ANALOG
|
||||
bool "Build Analog Device HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include Analog Device HD-audio codec support in
|
||||
snd-hda-intel driver, such as AD1986A.
|
||||
@@ -110,6 +112,7 @@ config SND_HDA_CODEC_ANALOG
|
||||
config SND_HDA_CODEC_SIGMATEL
|
||||
bool "Build IDT/Sigmatel HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include IDT (Sigmatel) HD-audio codec support in
|
||||
snd-hda-intel driver, such as STAC9200.
|
||||
@@ -122,6 +125,7 @@ config SND_HDA_CODEC_SIGMATEL
|
||||
config SND_HDA_CODEC_VIA
|
||||
bool "Build VIA HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include VIA HD-audio codec support in
|
||||
snd-hda-intel driver, such as VT1708.
|
||||
@@ -147,8 +151,8 @@ config SND_HDA_CODEC_HDMI
|
||||
|
||||
config SND_HDA_CODEC_CIRRUS
|
||||
bool "Build Cirrus Logic codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include Cirrus Logic codec support in
|
||||
snd-hda-intel driver, such as CS4206.
|
||||
@@ -161,6 +165,7 @@ config SND_HDA_CODEC_CIRRUS
|
||||
config SND_HDA_CODEC_CONEXANT
|
||||
bool "Build Conexant HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include Conexant HD-audio codec support in
|
||||
snd-hda-intel driver, such as CX20549.
|
||||
@@ -172,8 +177,8 @@ config SND_HDA_CODEC_CONEXANT
|
||||
|
||||
config SND_HDA_CODEC_CA0110
|
||||
bool "Build Creative CA0110-IBG codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include Creative CA0110-IBG codec support in
|
||||
snd-hda-intel driver, found on some Creative X-Fi cards.
|
||||
@@ -185,7 +190,6 @@ config SND_HDA_CODEC_CA0110
|
||||
|
||||
config SND_HDA_CODEC_CA0132
|
||||
bool "Build Creative CA0132 codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Creative CA0132 codec support in
|
||||
@@ -199,6 +203,7 @@ config SND_HDA_CODEC_CA0132
|
||||
config SND_HDA_CODEC_CMEDIA
|
||||
bool "Build C-Media HD-audio codec support"
|
||||
default y
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y here to include C-Media HD-audio codec support in
|
||||
snd-hda-intel driver, such as CMI9880.
|
||||
|
||||
@@ -97,6 +97,28 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
|
||||
}
|
||||
}
|
||||
|
||||
/* check whether the given pin has a proper pin I/O capability bit */
|
||||
static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
|
||||
unsigned int dev)
|
||||
{
|
||||
unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
|
||||
|
||||
/* some old hardware don't return the proper pincaps */
|
||||
if (!pincap)
|
||||
return true;
|
||||
|
||||
switch (dev) {
|
||||
case AC_JACK_LINE_OUT:
|
||||
case AC_JACK_SPEAKER:
|
||||
case AC_JACK_HP_OUT:
|
||||
case AC_JACK_SPDIF_OUT:
|
||||
case AC_JACK_DIG_OTHER_OUT:
|
||||
return !!(pincap & AC_PINCAP_OUT);
|
||||
default:
|
||||
return !!(pincap & AC_PINCAP_IN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all pin widgets and store the useful pin nids to cfg
|
||||
*
|
||||
@@ -126,6 +148,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
|
||||
struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
|
||||
int i;
|
||||
|
||||
if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
|
||||
cond_flags = i;
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
memset(line_out, 0, sizeof(line_out));
|
||||
@@ -156,10 +181,14 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
|
||||
|
||||
/* workaround for buggy BIOS setups */
|
||||
if (dev == AC_JACK_LINE_OUT) {
|
||||
if (conn == AC_JACK_PORT_FIXED)
|
||||
if (conn == AC_JACK_PORT_FIXED ||
|
||||
conn == AC_JACK_PORT_BOTH)
|
||||
dev = AC_JACK_SPEAKER;
|
||||
}
|
||||
|
||||
if (!check_pincap_validity(codec, nid, dev))
|
||||
continue;
|
||||
|
||||
switch (dev) {
|
||||
case AC_JACK_LINE_OUT:
|
||||
seq = get_defcfg_sequence(def_conf);
|
||||
@@ -363,7 +392,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec,
|
||||
{
|
||||
unsigned int def_conf;
|
||||
static const char * const mic_names[] = {
|
||||
"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
|
||||
"Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
|
||||
};
|
||||
int attr;
|
||||
|
||||
@@ -394,6 +423,8 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec,
|
||||
return "SPDIF In";
|
||||
case AC_JACK_DIG_OTHER_IN:
|
||||
return "Digital In";
|
||||
case AC_JACK_HP_OUT:
|
||||
return "Headphone Mic";
|
||||
default:
|
||||
return "Misc";
|
||||
}
|
||||
@@ -552,6 +583,9 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define is_hdmi_cfg(conf) \
|
||||
(get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
|
||||
|
||||
/**
|
||||
* snd_hda_get_pin_label - Get a label for the given I/O pin
|
||||
*
|
||||
@@ -572,6 +606,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
||||
const char *name = NULL;
|
||||
int i;
|
||||
bool hdmi;
|
||||
|
||||
if (indexp)
|
||||
*indexp = 0;
|
||||
@@ -590,16 +625,18 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
|
||||
label, maxlen, indexp);
|
||||
case AC_JACK_SPDIF_OUT:
|
||||
case AC_JACK_DIG_OTHER_OUT:
|
||||
if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
|
||||
name = "HDMI";
|
||||
else
|
||||
name = "SPDIF";
|
||||
if (cfg && indexp) {
|
||||
i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
|
||||
cfg->dig_outs);
|
||||
if (i >= 0)
|
||||
*indexp = i;
|
||||
}
|
||||
hdmi = is_hdmi_cfg(def_conf);
|
||||
name = hdmi ? "HDMI" : "SPDIF";
|
||||
if (cfg && indexp)
|
||||
for (i = 0; i < cfg->dig_outs; i++) {
|
||||
hda_nid_t pin = cfg->dig_out_pins[i];
|
||||
unsigned int c;
|
||||
if (pin == nid)
|
||||
break;
|
||||
c = snd_hda_codec_get_pincfg(codec, pin);
|
||||
if (hdmi == is_hdmi_cfg(c))
|
||||
(*indexp)++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (cfg) {
|
||||
@@ -622,28 +659,27 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
|
||||
|
||||
int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
|
||||
const struct hda_verb *list)
|
||||
int snd_hda_add_verbs(struct hda_codec *codec,
|
||||
const struct hda_verb *list)
|
||||
{
|
||||
const struct hda_verb **v;
|
||||
v = snd_array_new(&spec->verbs);
|
||||
v = snd_array_new(&codec->verbs);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
*v = list;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
|
||||
EXPORT_SYMBOL_HDA(snd_hda_add_verbs);
|
||||
|
||||
void snd_hda_gen_apply_verbs(struct hda_codec *codec)
|
||||
void snd_hda_apply_verbs(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
int i;
|
||||
for (i = 0; i < spec->verbs.used; i++) {
|
||||
struct hda_verb **v = snd_array_elem(&spec->verbs, i);
|
||||
for (i = 0; i < codec->verbs.used; i++) {
|
||||
struct hda_verb **v = snd_array_elem(&codec->verbs, i);
|
||||
snd_hda_sequence_write(codec, *v);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
|
||||
EXPORT_SYMBOL_HDA(snd_hda_apply_verbs);
|
||||
|
||||
void snd_hda_apply_pincfgs(struct hda_codec *codec,
|
||||
const struct hda_pintbl *cfg)
|
||||
@@ -653,20 +689,26 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
|
||||
|
||||
static void set_pin_targets(struct hda_codec *codec,
|
||||
const struct hda_pintbl *cfg)
|
||||
{
|
||||
for (; cfg->nid; cfg++)
|
||||
snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
|
||||
}
|
||||
|
||||
void snd_hda_apply_fixup(struct hda_codec *codec, int action)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
int id = spec->fixup_id;
|
||||
int id = codec->fixup_id;
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
const char *modelname = spec->fixup_name;
|
||||
const char *modelname = codec->fixup_name;
|
||||
#endif
|
||||
int depth = 0;
|
||||
|
||||
if (!spec->fixup_list)
|
||||
if (!codec->fixup_list)
|
||||
return;
|
||||
|
||||
while (id >= 0) {
|
||||
const struct hda_fixup *fix = spec->fixup_list + id;
|
||||
const struct hda_fixup *fix = codec->fixup_list + id;
|
||||
|
||||
switch (fix->type) {
|
||||
case HDA_FIXUP_PINS:
|
||||
@@ -683,7 +725,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
|
||||
snd_printdd(KERN_INFO SFX
|
||||
"%s: Apply fix-verbs for %s\n",
|
||||
codec->chip_name, modelname);
|
||||
snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
|
||||
snd_hda_add_verbs(codec, fix->v.verbs);
|
||||
break;
|
||||
case HDA_FIXUP_FUNC:
|
||||
if (!fix->v.func)
|
||||
@@ -693,6 +735,14 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
|
||||
codec->chip_name, modelname);
|
||||
fix->v.func(codec, fix, action);
|
||||
break;
|
||||
case HDA_FIXUP_PINCTLS:
|
||||
if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
|
||||
break;
|
||||
snd_printdd(KERN_INFO SFX
|
||||
"%s: Apply pinctl for %s\n",
|
||||
codec->chip_name, modelname);
|
||||
set_pin_targets(codec, fix->v.pins);
|
||||
break;
|
||||
default:
|
||||
snd_printk(KERN_ERR SFX
|
||||
"%s: Invalid fixup type %d\n",
|
||||
@@ -713,15 +763,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
||||
const struct snd_pci_quirk *quirk,
|
||||
const struct hda_fixup *fixlist)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const struct snd_pci_quirk *q;
|
||||
int id = -1;
|
||||
const char *name = NULL;
|
||||
|
||||
/* when model=nofixup is given, don't pick up any fixups */
|
||||
if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
|
||||
spec->fixup_list = NULL;
|
||||
spec->fixup_id = -1;
|
||||
codec->fixup_list = NULL;
|
||||
codec->fixup_id = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -759,10 +808,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
spec->fixup_id = id;
|
||||
codec->fixup_id = id;
|
||||
if (id >= 0) {
|
||||
spec->fixup_list = fixlist;
|
||||
spec->fixup_name = name;
|
||||
codec->fixup_list = fixlist;
|
||||
codec->fixup_name = name;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
|
||||
|
||||
@@ -51,8 +51,9 @@ enum {
|
||||
INPUT_PIN_ATTR_INT, /* internal mic/line-in */
|
||||
INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
|
||||
INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
|
||||
INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
|
||||
INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
|
||||
INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
|
||||
INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
|
||||
};
|
||||
|
||||
int snd_hda_get_input_pin_attr(unsigned int def_conf);
|
||||
@@ -89,82 +90,4 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
|
||||
#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
|
||||
snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
struct hda_gen_spec {
|
||||
/* fix-up list */
|
||||
int fixup_id;
|
||||
const struct hda_fixup *fixup_list;
|
||||
const char *fixup_name;
|
||||
|
||||
/* additional init verbs */
|
||||
struct snd_array verbs;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Fix-up pin default configurations and add default verbs
|
||||
*/
|
||||
|
||||
struct hda_pintbl {
|
||||
hda_nid_t nid;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
struct hda_model_fixup {
|
||||
const int id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct hda_fixup {
|
||||
int type;
|
||||
bool chained;
|
||||
int chain_id;
|
||||
union {
|
||||
const struct hda_pintbl *pins;
|
||||
const struct hda_verb *verbs;
|
||||
void (*func)(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action);
|
||||
} v;
|
||||
};
|
||||
|
||||
/* fixup types */
|
||||
enum {
|
||||
HDA_FIXUP_INVALID,
|
||||
HDA_FIXUP_PINS,
|
||||
HDA_FIXUP_VERBS,
|
||||
HDA_FIXUP_FUNC,
|
||||
};
|
||||
|
||||
/* fixup action definitions */
|
||||
enum {
|
||||
HDA_FIXUP_ACT_PRE_PROBE,
|
||||
HDA_FIXUP_ACT_PROBE,
|
||||
HDA_FIXUP_ACT_INIT,
|
||||
HDA_FIXUP_ACT_BUILD,
|
||||
};
|
||||
|
||||
int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
|
||||
const struct hda_verb *list);
|
||||
void snd_hda_gen_apply_verbs(struct hda_codec *codec);
|
||||
void snd_hda_apply_pincfgs(struct hda_codec *codec,
|
||||
const struct hda_pintbl *cfg);
|
||||
void snd_hda_apply_fixup(struct hda_codec *codec, int action);
|
||||
void snd_hda_pick_fixup(struct hda_codec *codec,
|
||||
const struct hda_model_fixup *models,
|
||||
const struct snd_pci_quirk *quirk,
|
||||
const struct hda_fixup *fixlist);
|
||||
|
||||
static inline void snd_hda_gen_init(struct hda_gen_spec *spec)
|
||||
{
|
||||
snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
|
||||
}
|
||||
|
||||
static inline void snd_hda_gen_free(struct hda_gen_spec *spec)
|
||||
{
|
||||
snd_array_free(&spec->verbs);
|
||||
}
|
||||
|
||||
#endif /* __SOUND_HDA_AUTO_PARSER_H */
|
||||
|
||||
+344
-133
@@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
|
||||
again:
|
||||
snd_hda_power_up(codec);
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
trace_hda_send_cmd(codec, cmd);
|
||||
err = bus->ops.command(bus, cmd);
|
||||
for (;;) {
|
||||
trace_hda_send_cmd(codec, cmd);
|
||||
err = bus->ops.command(bus, cmd);
|
||||
if (err != -EAGAIN)
|
||||
break;
|
||||
/* process pending verbs */
|
||||
bus->ops.get_response(bus, codec->addr);
|
||||
}
|
||||
if (!err && res) {
|
||||
*res = bus->ops.get_response(bus, codec->addr);
|
||||
trace_hda_get_response(codec, *res);
|
||||
@@ -328,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
|
||||
|
||||
/* connection list element */
|
||||
struct hda_conn_list {
|
||||
struct list_head list;
|
||||
int len;
|
||||
hda_nid_t nid;
|
||||
hda_nid_t conns[0];
|
||||
};
|
||||
|
||||
/* look up the cached results */
|
||||
static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
|
||||
static struct hda_conn_list *
|
||||
lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
int i, len;
|
||||
for (i = 0; i < array->used; ) {
|
||||
hda_nid_t *p = snd_array_elem(array, i);
|
||||
if (nid == *p)
|
||||
struct hda_conn_list *p;
|
||||
list_for_each_entry(p, &codec->conn_list, list) {
|
||||
if (p->nid == nid)
|
||||
return p;
|
||||
len = p[1];
|
||||
i += len + 2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
|
||||
const hda_nid_t *list)
|
||||
{
|
||||
struct hda_conn_list *p;
|
||||
|
||||
p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
p->len = len;
|
||||
p->nid = nid;
|
||||
memcpy(p->conns, list, len * sizeof(hda_nid_t));
|
||||
list_add(&p->list, &codec->conn_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_conn_list(struct hda_codec *codec)
|
||||
{
|
||||
while (!list_empty(&codec->conn_list)) {
|
||||
struct hda_conn_list *p;
|
||||
p = list_first_entry(&codec->conn_list, typeof(*p), list);
|
||||
list_del(&p->list);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* read the connection and add to the cache */
|
||||
static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
@@ -354,6 +391,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
|
||||
return snd_hda_override_conn_list(codec, nid, len, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_get_conn_list - get connection list
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to parse
|
||||
* @len: number of connection list entries
|
||||
* @listp: the pointer to store NID list
|
||||
*
|
||||
* Parses the connection list of the given widget and stores the pointer
|
||||
* to the list of NIDs.
|
||||
*
|
||||
* Returns the number of connections, or a negative error code.
|
||||
*
|
||||
* Note that the returned pointer isn't protected against the list
|
||||
* modification. If snd_hda_override_conn_list() might be called
|
||||
* concurrently, protect with a mutex appropriately.
|
||||
*/
|
||||
int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
|
||||
const hda_nid_t **listp)
|
||||
{
|
||||
bool added = false;
|
||||
|
||||
for (;;) {
|
||||
int err;
|
||||
const struct hda_conn_list *p;
|
||||
|
||||
/* if the connection-list is already cached, read it */
|
||||
p = lookup_conn_list(codec, nid);
|
||||
if (p) {
|
||||
if (listp)
|
||||
*listp = p->conns;
|
||||
return p->len;
|
||||
}
|
||||
if (snd_BUG_ON(added))
|
||||
return -EINVAL;
|
||||
|
||||
err = read_and_add_raw_conns(codec, nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
|
||||
|
||||
/**
|
||||
* snd_hda_get_connections - copy connection list
|
||||
* @codec: the HDA codec
|
||||
@@ -369,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
|
||||
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
hda_nid_t *conn_list, int max_conns)
|
||||
{
|
||||
struct snd_array *array = &codec->conn_lists;
|
||||
int len;
|
||||
hda_nid_t *p;
|
||||
bool added = false;
|
||||
const hda_nid_t *list;
|
||||
int len = snd_hda_get_conn_list(codec, nid, &list);
|
||||
|
||||
again:
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
len = -1;
|
||||
/* if the connection-list is already cached, read it */
|
||||
p = lookup_conn_list(array, nid);
|
||||
if (p) {
|
||||
len = p[1];
|
||||
if (conn_list && len > max_conns) {
|
||||
if (len > 0 && conn_list) {
|
||||
if (len > max_conns) {
|
||||
snd_printk(KERN_ERR "hda_codec: "
|
||||
"Too many connections %d for NID 0x%x\n",
|
||||
len, nid);
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (conn_list && len)
|
||||
memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
|
||||
memcpy(conn_list, list, len * sizeof(hda_nid_t));
|
||||
}
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
if (len >= 0)
|
||||
return len;
|
||||
if (snd_BUG_ON(added))
|
||||
return -EINVAL;
|
||||
|
||||
len = read_and_add_raw_conns(codec, nid);
|
||||
if (len < 0)
|
||||
return len;
|
||||
added = true;
|
||||
goto again;
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
|
||||
|
||||
@@ -424,6 +485,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int shift, num_elems, mask;
|
||||
unsigned int wcaps;
|
||||
hda_nid_t prev_nid;
|
||||
int null_count = 0;
|
||||
|
||||
if (snd_BUG_ON(!conn_list || max_conns <= 0))
|
||||
return -EINVAL;
|
||||
@@ -474,7 +536,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
}
|
||||
range_val = !!(parm & (1 << (shift-1))); /* ranges */
|
||||
val = parm & mask;
|
||||
if (val == 0) {
|
||||
if (val == 0 && null_count++) { /* no second chance */
|
||||
snd_printk(KERN_WARNING "hda_codec: "
|
||||
"invalid CONNECT_LIST verb %x[%i]:%x\n",
|
||||
nid, i, parm);
|
||||
@@ -512,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
return conns;
|
||||
}
|
||||
|
||||
static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
|
||||
{
|
||||
hda_nid_t *p = snd_array_new(array);
|
||||
if (!p)
|
||||
return false;
|
||||
*p = nid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_override_conn_list - add/modify the connection-list to cache
|
||||
* @codec: the HDA codec
|
||||
@@ -536,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
|
||||
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
|
||||
const hda_nid_t *list)
|
||||
{
|
||||
struct snd_array *array = &codec->conn_lists;
|
||||
hda_nid_t *p;
|
||||
int i, old_used;
|
||||
struct hda_conn_list *p;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
p = lookup_conn_list(array, nid);
|
||||
if (p)
|
||||
*p = -1; /* invalidate the old entry */
|
||||
p = lookup_conn_list(codec, nid);
|
||||
if (p) {
|
||||
list_del(&p->list);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
old_used = array->used;
|
||||
if (!add_conn_list(array, nid) || !add_conn_list(array, len))
|
||||
goto error_add;
|
||||
for (i = 0; i < len; i++)
|
||||
if (!add_conn_list(array, list[i]))
|
||||
goto error_add;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
|
||||
error_add:
|
||||
array->used = old_used;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return -ENOMEM;
|
||||
return add_conn_list(codec, nid, len, list);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
|
||||
|
||||
@@ -575,16 +615,16 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
|
||||
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
|
||||
hda_nid_t nid, int recursive)
|
||||
{
|
||||
hda_nid_t conn[HDA_MAX_NUM_INPUTS];
|
||||
const hda_nid_t *conn;
|
||||
int i, nums;
|
||||
|
||||
nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
|
||||
nums = snd_hda_get_conn_list(codec, mux, &conn);
|
||||
for (i = 0; i < nums; i++)
|
||||
if (conn[i] == nid)
|
||||
return i;
|
||||
if (!recursive)
|
||||
return -1;
|
||||
if (recursive > 5) {
|
||||
if (recursive > 10) {
|
||||
snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
|
||||
return -1;
|
||||
}
|
||||
@@ -1046,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
|
||||
struct hda_pincfg *pin;
|
||||
|
||||
#ifdef CONFIG_SND_HDA_HWDEP
|
||||
pin = look_up_pincfg(codec, &codec->user_pins, nid);
|
||||
if (pin)
|
||||
return pin->cfg;
|
||||
{
|
||||
unsigned int cfg = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
pin = look_up_pincfg(codec, &codec->user_pins, nid);
|
||||
if (pin)
|
||||
cfg = pin->cfg;
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
if (cfg)
|
||||
return cfg;
|
||||
}
|
||||
#endif
|
||||
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
|
||||
if (pin)
|
||||
@@ -1060,6 +1107,32 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
|
||||
|
||||
/* remember the current pinctl target value */
|
||||
int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int val)
|
||||
{
|
||||
struct hda_pincfg *pin;
|
||||
|
||||
pin = look_up_pincfg(codec, &codec->init_pins, nid);
|
||||
if (!pin)
|
||||
return -EINVAL;
|
||||
pin->target = val;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target);
|
||||
|
||||
/* return the current pinctl target value */
|
||||
int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct hda_pincfg *pin;
|
||||
|
||||
pin = look_up_pincfg(codec, &codec->init_pins, nid);
|
||||
if (!pin)
|
||||
return 0;
|
||||
return pin->target;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target);
|
||||
|
||||
/**
|
||||
* snd_hda_shutup_pins - Shut up all pins
|
||||
* @codec: the HDA codec
|
||||
@@ -1179,8 +1252,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
|
||||
snd_array_free(&codec->mixers);
|
||||
snd_array_free(&codec->nids);
|
||||
snd_array_free(&codec->cvt_setups);
|
||||
snd_array_free(&codec->conn_lists);
|
||||
snd_array_free(&codec->spdif_out);
|
||||
remove_conn_list(codec);
|
||||
codec->bus->caddr_tbl[codec->addr] = NULL;
|
||||
if (codec->patch_ops.free)
|
||||
codec->patch_ops.free(codec);
|
||||
@@ -1250,9 +1323,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
|
||||
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
|
||||
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
|
||||
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
|
||||
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
|
||||
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
|
||||
snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
|
||||
snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
|
||||
INIT_LIST_HEAD(&codec->conn_list);
|
||||
|
||||
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@@ -1451,7 +1526,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
"NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
|
||||
nid, stream_tag, channel_id, format);
|
||||
p = get_hda_cvt_setup(codec, nid);
|
||||
if (!p)
|
||||
if (!p || p->active)
|
||||
return;
|
||||
|
||||
if (codec->pcm_format_first)
|
||||
@@ -1498,7 +1573,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
|
||||
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
|
||||
p = get_hda_cvt_setup(codec, nid);
|
||||
if (p) {
|
||||
if (p && p->active) {
|
||||
/* here we just clear the active flag when do_now isn't set;
|
||||
* actual clean-ups will be done later in
|
||||
* purify_inactive_streams() called from snd_hda_codec_prpapre()
|
||||
@@ -1610,6 +1685,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
|
||||
cur = snd_array_index(&cache->buf, info);
|
||||
info->key = key;
|
||||
info->val = 0;
|
||||
info->dirty = 0;
|
||||
idx = key % (u16)ARRAY_SIZE(cache->hash);
|
||||
info->next = cache->hash[idx];
|
||||
cache->hash[idx] = cur;
|
||||
@@ -1764,7 +1840,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
|
||||
*/
|
||||
static struct hda_amp_info *
|
||||
update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int index)
|
||||
int direction, int index, bool init_only)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int parm, val = 0;
|
||||
@@ -1790,14 +1866,15 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
}
|
||||
info->vol[ch] = val;
|
||||
info->head.val |= INFO_AMP_VOL(ch);
|
||||
}
|
||||
} else if (init_only)
|
||||
return NULL;
|
||||
return info;
|
||||
}
|
||||
|
||||
/*
|
||||
* write the current volume in info to the h/w
|
||||
*/
|
||||
static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
|
||||
static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
|
||||
hda_nid_t nid, int ch, int direction, int index,
|
||||
int val)
|
||||
{
|
||||
@@ -1806,8 +1883,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
|
||||
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
|
||||
parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
|
||||
parm |= index << AC_AMP_SET_INDEX_SHIFT;
|
||||
if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
|
||||
(info->amp_caps & AC_AMPCAP_MIN_MUTE))
|
||||
if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
|
||||
(amp_caps & AC_AMPCAP_MIN_MUTE))
|
||||
; /* set the zero value as a fake mute */
|
||||
else
|
||||
parm |= val;
|
||||
@@ -1831,7 +1908,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
unsigned int val = 0;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = update_amp_hash(codec, nid, ch, direction, index);
|
||||
info = update_amp_hash(codec, nid, ch, direction, index, false);
|
||||
if (info)
|
||||
val = info->vol[ch];
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
@@ -1839,6 +1916,38 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
|
||||
|
||||
static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val,
|
||||
bool init_only)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int caps;
|
||||
unsigned int cache_only;
|
||||
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
val &= mask;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
|
||||
if (!info) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
val |= info->vol[ch] & ~mask;
|
||||
if (info->vol[ch] == val) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
info->vol[ch] = val;
|
||||
cache_only = info->head.dirty = codec->cached_write;
|
||||
caps = info->amp_caps;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
if (!cache_only)
|
||||
put_vol_mute(codec, caps, nid, ch, direction, idx, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_amp_update - update the AMP value
|
||||
* @codec: HD-audio codec
|
||||
@@ -1855,27 +1964,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
|
||||
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;
|
||||
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
val &= mask;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = update_amp_hash(codec, nid, ch, direction, idx);
|
||||
if (!info) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
val |= info->vol[ch] & ~mask;
|
||||
if (info->vol[ch] == val) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
info->vol[ch] = val;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
put_vol_mute(codec, info, nid, ch, direction, idx, val);
|
||||
return 1;
|
||||
return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
|
||||
|
||||
@@ -1905,7 +1994,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Works like snd_hda_codec_amp_update() but it writes the value only at
|
||||
* the first access. If the amp was already initialized / updated beforehand,
|
||||
* this does nothing.
|
||||
*/
|
||||
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int dir, int idx, int mask, int val)
|
||||
{
|
||||
return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init);
|
||||
|
||||
int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val)
|
||||
{
|
||||
int ch, ret = 0;
|
||||
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
for (ch = 0; ch < 2; ch++)
|
||||
ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
|
||||
idx, mask, val);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
|
||||
* @codec: HD-audio codec
|
||||
@@ -1914,28 +2027,40 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
|
||||
*/
|
||||
void snd_hda_codec_resume_amp(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_amp_info *buffer = codec->amp_cache.buf.list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
|
||||
u32 key = buffer->head.key;
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
codec->cached_write = 0;
|
||||
for (i = 0; i < codec->amp_cache.buf.used; i++) {
|
||||
struct hda_amp_info *buffer;
|
||||
u32 key;
|
||||
hda_nid_t nid;
|
||||
unsigned int idx, dir, ch;
|
||||
struct hda_amp_info info;
|
||||
|
||||
buffer = snd_array_elem(&codec->amp_cache.buf, i);
|
||||
if (!buffer->head.dirty)
|
||||
continue;
|
||||
buffer->head.dirty = 0;
|
||||
info = *buffer;
|
||||
key = info.head.key;
|
||||
if (!key)
|
||||
continue;
|
||||
nid = key & 0xff;
|
||||
idx = (key >> 16) & 0xff;
|
||||
dir = (key >> 24) & 0xff;
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
if (!(buffer->head.val & INFO_AMP_VOL(ch)))
|
||||
if (!(info.head.val & INFO_AMP_VOL(ch)))
|
||||
continue;
|
||||
put_vol_mute(codec, buffer, nid, ch, dir, idx,
|
||||
buffer->vol[ch]);
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
|
||||
info.vol[ch]);
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int ofs)
|
||||
@@ -2362,6 +2487,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
|
||||
snd_array_free(&codec->driver_pins);
|
||||
snd_array_free(&codec->cvt_setups);
|
||||
snd_array_free(&codec->spdif_out);
|
||||
snd_array_free(&codec->verbs);
|
||||
codec->num_pcms = 0;
|
||||
codec->pcm_info = NULL;
|
||||
codec->preset = NULL;
|
||||
@@ -3375,12 +3501,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* command cache
|
||||
*/
|
||||
|
||||
/* build a 32bit cache key with the widget id and the command parameter */
|
||||
/* build a 31bit cache key with the widget id and the command parameter */
|
||||
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
|
||||
#define get_cmd_cache_nid(key) ((key) & 0xff)
|
||||
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
|
||||
@@ -3400,20 +3525,28 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
|
||||
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
|
||||
int err;
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
unsigned int cache_only;
|
||||
|
||||
cache_only = codec->cached_write;
|
||||
if (!cache_only) {
|
||||
err = snd_hda_codec_write(codec, nid, direct, verb, parm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
mutex_lock(&codec->bus->cmd_mutex);
|
||||
c = get_alloc_hash(&codec->cmd_cache, key);
|
||||
if (c)
|
||||
if (c) {
|
||||
c->val = parm;
|
||||
c->dirty = cache_only;
|
||||
}
|
||||
mutex_unlock(&codec->bus->cmd_mutex);
|
||||
return 0;
|
||||
}
|
||||
@@ -3462,16 +3595,27 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
|
||||
*/
|
||||
void snd_hda_codec_resume_cache(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
|
||||
u32 key = buffer->key;
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
codec->cached_write = 0;
|
||||
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
|
||||
struct hda_cache_head *buffer;
|
||||
u32 key;
|
||||
|
||||
buffer = snd_array_elem(&codec->cmd_cache.buf, i);
|
||||
key = buffer->key;
|
||||
if (!key)
|
||||
continue;
|
||||
if (!buffer->dirty)
|
||||
continue;
|
||||
buffer->dirty = 0;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
|
||||
get_cmd_cache_cmd(key), buffer->val);
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
}
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
|
||||
|
||||
@@ -3492,7 +3636,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
seq->param);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/**
|
||||
* snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
|
||||
* @codec: HD-audio codec
|
||||
*/
|
||||
void snd_hda_codec_flush_cache(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache);
|
||||
|
||||
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
|
||||
unsigned int power_state,
|
||||
@@ -3640,6 +3794,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
|
||||
return state;
|
||||
}
|
||||
|
||||
/* mark all entries of cmd and amp caches dirty */
|
||||
static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
|
||||
struct hda_cache_head *cmd;
|
||||
cmd = snd_array_elem(&codec->cmd_cache.buf, i);
|
||||
cmd->dirty = 1;
|
||||
}
|
||||
for (i = 0; i < codec->amp_cache.buf.used; i++) {
|
||||
struct hda_amp_info *amp;
|
||||
amp = snd_array_elem(&codec->amp_cache.buf, i);
|
||||
amp->head.dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* kick up codec; used both from PM and power-save
|
||||
*/
|
||||
@@ -3647,6 +3817,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
|
||||
{
|
||||
codec->in_pm = 1;
|
||||
|
||||
hda_mark_cmd_cache_dirty(codec);
|
||||
|
||||
/* set as if powered on for avoiding re-entering the resume
|
||||
* in the resume / power-save sequence
|
||||
*/
|
||||
@@ -5121,23 +5293,62 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
|
||||
|
||||
/* correct the pin ctl value for matching with the pin cap */
|
||||
unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
|
||||
hda_nid_t pin, unsigned int val)
|
||||
{
|
||||
static unsigned int cap_lists[][2] = {
|
||||
{ AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
|
||||
{ AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
|
||||
{ AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
|
||||
{ AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
|
||||
};
|
||||
unsigned int cap;
|
||||
|
||||
if (!val)
|
||||
return 0;
|
||||
cap = snd_hda_query_pin_caps(codec, pin);
|
||||
if (!cap)
|
||||
return val; /* don't know what to do... */
|
||||
|
||||
if (val & AC_PINCTL_OUT_EN) {
|
||||
if (!(cap & AC_PINCAP_OUT))
|
||||
val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
|
||||
else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
|
||||
val &= ~AC_PINCTL_HP_EN;
|
||||
}
|
||||
|
||||
if (val & AC_PINCTL_IN_EN) {
|
||||
if (!(cap & AC_PINCAP_IN))
|
||||
val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
|
||||
else {
|
||||
unsigned int vcap, vref;
|
||||
int i;
|
||||
vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
|
||||
vref = val & AC_PINCTL_VREFEN;
|
||||
for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
|
||||
if (vref == cap_lists[i][0] &&
|
||||
!(vcap & cap_lists[i][1])) {
|
||||
if (i == ARRAY_SIZE(cap_lists) - 1)
|
||||
vref = AC_PINCTL_VREF_HIZ;
|
||||
else
|
||||
vref = cap_lists[i + 1][0];
|
||||
}
|
||||
}
|
||||
val &= ~AC_PINCTL_VREFEN;
|
||||
val |= vref;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl);
|
||||
|
||||
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
|
||||
unsigned int val, bool cached)
|
||||
{
|
||||
if (val) {
|
||||
unsigned int cap = snd_hda_query_pin_caps(codec, pin);
|
||||
if (cap && (val & AC_PINCTL_OUT_EN)) {
|
||||
if (!(cap & AC_PINCAP_OUT))
|
||||
val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
|
||||
else if ((val & AC_PINCTL_HP_EN) &&
|
||||
!(cap & AC_PINCAP_HP_DRV))
|
||||
val &= ~AC_PINCTL_HP_EN;
|
||||
}
|
||||
if (cap && (val & AC_PINCTL_IN_EN)) {
|
||||
if (!(cap & AC_PINCAP_IN))
|
||||
val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
|
||||
}
|
||||
}
|
||||
val = snd_hda_correct_pin_ctl(codec, pin, val);
|
||||
snd_hda_codec_set_pin_target(codec, pin, val);
|
||||
if (cached)
|
||||
return snd_hda_codec_update_cache(codec, pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, val);
|
||||
|
||||
+22
-11
@@ -719,9 +719,10 @@ struct hda_codec_ops {
|
||||
|
||||
/* record for amp information cache */
|
||||
struct hda_cache_head {
|
||||
u32 key; /* hash key */
|
||||
u32 key:31; /* hash key */
|
||||
u32 dirty:1;
|
||||
u16 val; /* assigned value */
|
||||
u16 next; /* next link; -1 = terminal */
|
||||
u16 next;
|
||||
};
|
||||
|
||||
struct hda_amp_info {
|
||||
@@ -830,7 +831,7 @@ struct hda_codec {
|
||||
struct hda_cache_rec amp_cache; /* cache for amp access */
|
||||
struct hda_cache_rec cmd_cache; /* cache for other commands */
|
||||
|
||||
struct snd_array conn_lists; /* connection-list array */
|
||||
struct list_head conn_list; /* linked-list of connection-list */
|
||||
|
||||
struct mutex spdif_mutex;
|
||||
struct mutex control_mutex;
|
||||
@@ -844,6 +845,7 @@ struct hda_codec {
|
||||
struct snd_array cvt_setups; /* audio convert setups */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_HWDEP
|
||||
struct mutex user_mutex;
|
||||
struct snd_hwdep *hwdep; /* assigned hwdep device */
|
||||
struct snd_array init_verbs; /* additional init verbs */
|
||||
struct snd_array hints; /* additional hints */
|
||||
@@ -865,8 +867,11 @@ struct hda_codec {
|
||||
unsigned int pins_shutup:1; /* pins are shut up */
|
||||
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
|
||||
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
|
||||
unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
|
||||
unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */
|
||||
unsigned int pcm_format_first:1; /* PCM format must be set first */
|
||||
unsigned int epss:1; /* supporting EPSS? */
|
||||
unsigned int cached_write:1; /* write only to caches */
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_on :1; /* current (global) power-state */
|
||||
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
|
||||
@@ -894,6 +899,14 @@ struct hda_codec {
|
||||
/* jack detection */
|
||||
struct snd_array jacks;
|
||||
#endif
|
||||
|
||||
/* fix-up list */
|
||||
int fixup_id;
|
||||
const struct hda_fixup *fixup_list;
|
||||
const char *fixup_name;
|
||||
|
||||
/* additional init verbs */
|
||||
struct snd_array verbs;
|
||||
};
|
||||
|
||||
/* direction */
|
||||
@@ -932,6 +945,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
|
||||
}
|
||||
int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
hda_nid_t *conn_list, int max_conns);
|
||||
int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
|
||||
const hda_nid_t **listp);
|
||||
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
|
||||
const hda_nid_t *list);
|
||||
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
|
||||
@@ -952,7 +967,6 @@ void snd_hda_sequence_write(struct hda_codec *codec,
|
||||
int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
|
||||
|
||||
/* cached write */
|
||||
#ifdef CONFIG_PM
|
||||
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
@@ -960,17 +974,14 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_codec_resume_cache(struct hda_codec *codec);
|
||||
#else
|
||||
#define snd_hda_codec_write_cache snd_hda_codec_write
|
||||
#define snd_hda_codec_update_cache snd_hda_codec_write
|
||||
#define snd_hda_sequence_write_cache snd_hda_sequence_write
|
||||
#endif
|
||||
/* both for cmd & amp caches */
|
||||
void snd_hda_codec_flush_cache(struct hda_codec *codec);
|
||||
|
||||
/* the struct for codec->pin_configs */
|
||||
struct hda_pincfg {
|
||||
hda_nid_t nid;
|
||||
unsigned char ctrl; /* current pin control value */
|
||||
unsigned char pad; /* reserved */
|
||||
unsigned char ctrl; /* original pin control value */
|
||||
unsigned char target; /* target pin control value */
|
||||
unsigned int cfg; /* default configuration */
|
||||
};
|
||||
|
||||
|
||||
+4619
-852
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Generic BIOS auto-parser helper functions for HD-audio
|
||||
*
|
||||
* Copyright (c) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_HDA_GENERIC_H
|
||||
#define __SOUND_HDA_GENERIC_H
|
||||
|
||||
/* unsol event tags */
|
||||
enum {
|
||||
HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
|
||||
HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
|
||||
};
|
||||
|
||||
/* table entry for multi-io paths */
|
||||
struct hda_multi_io {
|
||||
hda_nid_t pin; /* multi-io widget pin NID */
|
||||
hda_nid_t dac; /* DAC to be connected */
|
||||
unsigned int ctl_in; /* cached input-pin control value */
|
||||
};
|
||||
|
||||
/* Widget connection path
|
||||
*
|
||||
* For output, stored in the order of DAC -> ... -> pin,
|
||||
* for input, pin -> ... -> ADC.
|
||||
*
|
||||
* idx[i] contains the source index number to select on of the widget path[i];
|
||||
* e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
|
||||
* multi[] indicates whether it's a selector widget with multi-connectors
|
||||
* (i.e. the connection selection is mandatory)
|
||||
* vol_ctl and mute_ctl contains the NIDs for the assigned mixers
|
||||
*/
|
||||
|
||||
#define MAX_NID_PATH_DEPTH 10
|
||||
|
||||
enum {
|
||||
NID_PATH_VOL_CTL,
|
||||
NID_PATH_MUTE_CTL,
|
||||
NID_PATH_BOOST_CTL,
|
||||
NID_PATH_NUM_CTLS
|
||||
};
|
||||
|
||||
struct nid_path {
|
||||
int depth;
|
||||
hda_nid_t path[MAX_NID_PATH_DEPTH];
|
||||
unsigned char idx[MAX_NID_PATH_DEPTH];
|
||||
unsigned char multi[MAX_NID_PATH_DEPTH];
|
||||
unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
|
||||
bool active;
|
||||
};
|
||||
|
||||
/* mic/line-in auto switching entry */
|
||||
|
||||
#define MAX_AUTO_MIC_PINS 3
|
||||
|
||||
struct automic_entry {
|
||||
hda_nid_t pin; /* pin */
|
||||
int idx; /* imux index, -1 = invalid */
|
||||
unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
|
||||
};
|
||||
|
||||
/* active stream id */
|
||||
enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
|
||||
|
||||
/* PCM hook action */
|
||||
enum {
|
||||
HDA_GEN_PCM_ACT_OPEN,
|
||||
HDA_GEN_PCM_ACT_PREPARE,
|
||||
HDA_GEN_PCM_ACT_CLEANUP,
|
||||
HDA_GEN_PCM_ACT_CLOSE,
|
||||
};
|
||||
|
||||
struct hda_gen_spec {
|
||||
char stream_name_analog[32]; /* analog PCM stream */
|
||||
const struct hda_pcm_stream *stream_analog_playback;
|
||||
const struct hda_pcm_stream *stream_analog_capture;
|
||||
|
||||
char stream_name_alt_analog[32]; /* alternative analog PCM stream */
|
||||
const struct hda_pcm_stream *stream_analog_alt_playback;
|
||||
const struct hda_pcm_stream *stream_analog_alt_capture;
|
||||
|
||||
char stream_name_digital[32]; /* digital PCM stream */
|
||||
const struct hda_pcm_stream *stream_digital_playback;
|
||||
const struct hda_pcm_stream *stream_digital_capture;
|
||||
|
||||
/* PCM */
|
||||
unsigned int active_streams;
|
||||
struct mutex pcm_mutex;
|
||||
|
||||
/* playback */
|
||||
struct hda_multi_out multiout; /* playback set-up
|
||||
* max_channels, dacs must be set
|
||||
* dig_out_nid and hp_nid are optional
|
||||
*/
|
||||
hda_nid_t alt_dac_nid;
|
||||
hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
|
||||
int dig_out_type;
|
||||
|
||||
/* capture */
|
||||
unsigned int num_adc_nids;
|
||||
hda_nid_t adc_nids[AUTO_CFG_MAX_INS];
|
||||
hda_nid_t dig_in_nid; /* digital-in NID; optional */
|
||||
hda_nid_t mixer_nid; /* analog-mixer NID */
|
||||
const char *input_labels[HDA_MAX_NUM_INPUTS];
|
||||
int input_label_idxs[HDA_MAX_NUM_INPUTS];
|
||||
|
||||
/* capture setup for dynamic dual-adc switch */
|
||||
hda_nid_t cur_adc;
|
||||
unsigned int cur_adc_stream_tag;
|
||||
unsigned int cur_adc_format;
|
||||
|
||||
/* capture source */
|
||||
struct hda_input_mux input_mux;
|
||||
unsigned int cur_mux[3];
|
||||
|
||||
/* channel model */
|
||||
/* min_channel_count contains the minimum channel count for primary
|
||||
* outputs. When multi_ios is set, the channels can be configured
|
||||
* between min_channel_count and (min_channel_count + multi_ios * 2).
|
||||
*
|
||||
* ext_channel_count contains the current channel count of the primary
|
||||
* out. This varies in the range above.
|
||||
*
|
||||
* Meanwhile, const_channel_count is the channel count for all outputs
|
||||
* including headphone and speakers. It's a constant value, and the
|
||||
* PCM is set up as max(ext_channel_count, const_channel_count).
|
||||
*/
|
||||
int min_channel_count; /* min. channel count for primary out */
|
||||
int ext_channel_count; /* current channel count for primary */
|
||||
int const_channel_count; /* channel count for all */
|
||||
|
||||
/* PCM information */
|
||||
struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
|
||||
|
||||
/* dynamic controls, init_verbs and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
struct snd_array kctls;
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
|
||||
unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
|
||||
hda_nid_t shared_mic_vref_pin;
|
||||
|
||||
/* DAC/ADC lists */
|
||||
int num_all_dacs;
|
||||
hda_nid_t all_dacs[16];
|
||||
int num_all_adcs;
|
||||
hda_nid_t all_adcs[AUTO_CFG_MAX_INS];
|
||||
|
||||
/* path list */
|
||||
struct snd_array paths;
|
||||
|
||||
/* path indices */
|
||||
int out_paths[AUTO_CFG_MAX_OUTS];
|
||||
int hp_paths[AUTO_CFG_MAX_OUTS];
|
||||
int speaker_paths[AUTO_CFG_MAX_OUTS];
|
||||
int aamix_out_paths[3];
|
||||
int digout_paths[AUTO_CFG_MAX_OUTS];
|
||||
int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS];
|
||||
int loopback_paths[HDA_MAX_NUM_INPUTS];
|
||||
int digin_path;
|
||||
|
||||
/* auto-mic stuff */
|
||||
int am_num_entries;
|
||||
struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
|
||||
|
||||
/* for pin sensing */
|
||||
/* current status; set in hda_geneic.c */
|
||||
unsigned int hp_jack_present:1;
|
||||
unsigned int line_jack_present:1;
|
||||
unsigned int speaker_muted:1; /* current status of speaker mute */
|
||||
unsigned int line_out_muted:1; /* current status of LO mute */
|
||||
|
||||
/* internal states of automute / autoswitch behavior */
|
||||
unsigned int auto_mic:1;
|
||||
unsigned int automute_speaker:1; /* automute speaker outputs */
|
||||
unsigned int automute_lo:1; /* automute LO outputs */
|
||||
|
||||
/* capabilities detected by parser */
|
||||
unsigned int detect_hp:1; /* Headphone detection enabled */
|
||||
unsigned int detect_lo:1; /* Line-out detection enabled */
|
||||
unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
|
||||
unsigned int automute_lo_possible:1; /* there are line outs and HP */
|
||||
|
||||
/* additional parameters set by codec drivers */
|
||||
unsigned int master_mute:1; /* master mute over all */
|
||||
unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
|
||||
unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
|
||||
|
||||
/* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
|
||||
unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
|
||||
unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
|
||||
|
||||
/* other parse behavior flags */
|
||||
unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
|
||||
unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
|
||||
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
|
||||
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
|
||||
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
|
||||
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
|
||||
unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
|
||||
unsigned int indep_hp:1; /* independent HP supported */
|
||||
unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
|
||||
unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
|
||||
unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
|
||||
unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
|
||||
|
||||
/* other internal flags */
|
||||
unsigned int no_analog:1; /* digital I/O only */
|
||||
unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
|
||||
unsigned int indep_hp_enabled:1; /* independent HP enabled */
|
||||
unsigned int have_aamix_ctl:1;
|
||||
|
||||
/* loopback mixing mode */
|
||||
bool aamix_mode;
|
||||
|
||||
/* for virtual master */
|
||||
hda_nid_t vmaster_nid;
|
||||
unsigned int vmaster_tlv[4];
|
||||
struct hda_vmaster_mute_hook vmaster_mute;
|
||||
#ifdef CONFIG_PM
|
||||
struct hda_loopback_check loopback;
|
||||
int num_loopbacks;
|
||||
struct hda_amp_list loopback_list[8];
|
||||
#endif
|
||||
|
||||
/* multi-io */
|
||||
int multi_ios;
|
||||
struct hda_multi_io multi_io[4];
|
||||
|
||||
/* hooks */
|
||||
void (*init_hook)(struct hda_codec *codec);
|
||||
void (*automute_hook)(struct hda_codec *codec);
|
||||
void (*cap_sync_hook)(struct hda_codec *codec,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
||||
/* PCM hooks */
|
||||
void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream,
|
||||
int action);
|
||||
void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream,
|
||||
int action);
|
||||
|
||||
/* automute / autoswitch hooks */
|
||||
void (*hp_automute_hook)(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *tbl);
|
||||
void (*line_automute_hook)(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *tbl);
|
||||
void (*mic_autoswitch_hook)(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *tbl);
|
||||
};
|
||||
|
||||
int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
|
||||
void snd_hda_gen_spec_free(struct hda_gen_spec *spec);
|
||||
|
||||
int snd_hda_gen_init(struct hda_codec *codec);
|
||||
void snd_hda_gen_free(struct hda_codec *codec);
|
||||
|
||||
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
|
||||
hda_nid_t from_nid, hda_nid_t to_nid);
|
||||
int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
|
||||
struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
|
||||
bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
hda_nid_t to_nid, int anchor_nid,
|
||||
struct nid_path *path);
|
||||
struct nid_path *
|
||||
snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
hda_nid_t to_nid, int anchor_nid);
|
||||
void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
|
||||
bool enable, bool add_aamix);
|
||||
|
||||
struct snd_kcontrol_new *
|
||||
snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
|
||||
const struct snd_kcontrol_new *temp);
|
||||
|
||||
int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
|
||||
struct auto_pin_cfg *cfg);
|
||||
int snd_hda_gen_build_controls(struct hda_codec *codec);
|
||||
int snd_hda_gen_build_pcms(struct hda_codec *codec);
|
||||
|
||||
/* standard jack event callbacks */
|
||||
void snd_hda_gen_hp_automute(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *jack);
|
||||
void snd_hda_gen_line_automute(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *jack);
|
||||
void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *jack);
|
||||
void snd_hda_gen_update_outputs(struct hda_codec *codec);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
|
||||
#endif
|
||||
|
||||
#endif /* __SOUND_HDA_GENERIC_H */
|
||||
+69
-18
@@ -148,6 +148,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
|
||||
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
|
||||
#endif
|
||||
|
||||
mutex_init(&codec->user_mutex);
|
||||
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
|
||||
snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
|
||||
snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
|
||||
@@ -346,12 +347,14 @@ static ssize_t init_verbs_show(struct device *dev,
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < codec->init_verbs.used; i++) {
|
||||
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
|
||||
len += snprintf(buf + len, PAGE_SIZE - len,
|
||||
"0x%02x 0x%03x 0x%04x\n",
|
||||
v->nid, v->verb, v->param);
|
||||
}
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -364,12 +367,16 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf)
|
||||
return -EINVAL;
|
||||
if (!nid || !verb)
|
||||
return -EINVAL;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
v = snd_array_new(&codec->init_verbs);
|
||||
if (!v)
|
||||
if (!v) {
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
v->nid = nid;
|
||||
v->verb = verb;
|
||||
v->param = param;
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -392,11 +399,13 @@ static ssize_t hints_show(struct device *dev,
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < codec->hints.used; i++) {
|
||||
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
|
||||
len += snprintf(buf + len, PAGE_SIZE - len,
|
||||
"%s = %s\n", hint->key, hint->val);
|
||||
}
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -431,6 +440,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
|
||||
{
|
||||
char *key, *val;
|
||||
struct hda_hint *hint;
|
||||
int err = 0;
|
||||
|
||||
buf = skip_spaces(buf);
|
||||
if (!*buf || *buf == '#' || *buf == '\n')
|
||||
@@ -450,26 +460,31 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
|
||||
val = skip_spaces(val);
|
||||
remove_trail_spaces(key);
|
||||
remove_trail_spaces(val);
|
||||
mutex_lock(&codec->user_mutex);
|
||||
hint = get_hint(codec, key);
|
||||
if (hint) {
|
||||
/* replace */
|
||||
kfree(hint->key);
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
return 0;
|
||||
goto unlock;
|
||||
}
|
||||
/* allocate a new hint entry */
|
||||
if (codec->hints.used >= MAX_HINTS)
|
||||
hint = NULL;
|
||||
else
|
||||
hint = snd_array_new(&codec->hints);
|
||||
if (!hint) {
|
||||
kfree(key);
|
||||
return -ENOMEM;
|
||||
if (hint) {
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
} else {
|
||||
err = -ENOMEM;
|
||||
}
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
if (err)
|
||||
kfree(key);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t hints_store(struct device *dev,
|
||||
@@ -489,11 +504,13 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
|
||||
char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < list->used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(list, i);
|
||||
len += sprintf(buf + len, "0x%02x 0x%08x\n",
|
||||
pin->nid, pin->cfg);
|
||||
}
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -528,13 +545,16 @@ static ssize_t driver_pin_configs_show(struct device *dev,
|
||||
|
||||
static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
|
||||
{
|
||||
int nid, cfg;
|
||||
int nid, cfg, err;
|
||||
|
||||
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
|
||||
return -EINVAL;
|
||||
if (!nid)
|
||||
return -EINVAL;
|
||||
return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
|
||||
mutex_lock(&codec->user_mutex);
|
||||
err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t user_pin_configs_store(struct device *dev,
|
||||
@@ -600,19 +620,50 @@ EXPORT_SYMBOL_HDA(snd_hda_get_hint);
|
||||
|
||||
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
|
||||
{
|
||||
const char *p = snd_hda_get_hint(codec, key);
|
||||
const char *p;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&codec->user_mutex);
|
||||
p = snd_hda_get_hint(codec, key);
|
||||
if (!p || !*p)
|
||||
return -ENOENT;
|
||||
switch (toupper(*p)) {
|
||||
case 'T': /* true */
|
||||
case 'Y': /* yes */
|
||||
case '1':
|
||||
return 1;
|
||||
ret = -ENOENT;
|
||||
else {
|
||||
switch (toupper(*p)) {
|
||||
case 'T': /* true */
|
||||
case 'Y': /* yes */
|
||||
case '1':
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
|
||||
|
||||
int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
|
||||
{
|
||||
const char *p;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&codec->user_mutex);
|
||||
p = snd_hda_get_hint(codec, key);
|
||||
if (!p)
|
||||
ret = -ENOENT;
|
||||
else if (strict_strtoul(p, 0, &val))
|
||||
ret = -EINVAL;
|
||||
else {
|
||||
*valp = val;
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&codec->user_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_int_hint);
|
||||
#endif /* CONFIG_SND_HDA_RECONFIG */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
|
||||
@@ -797,7 +797,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
unsigned int addr = azx_command_addr(val);
|
||||
unsigned int wp;
|
||||
unsigned int wp, rp;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
|
||||
@@ -806,11 +806,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
if (wp == 0xffff) {
|
||||
/* something wrong, controller likely turned to D3 */
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
wp++;
|
||||
wp %= ICH6_MAX_CORB_ENTRIES;
|
||||
|
||||
rp = azx_readw(chip, CORBRP);
|
||||
if (wp == rp) {
|
||||
/* oops, it's full */
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
chip->rirb.cmds[addr]++;
|
||||
chip->corb.buf[wp] = cpu_to_le32(val);
|
||||
azx_writel(chip, CORBWP, wp);
|
||||
|
||||
@@ -29,7 +29,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
|
||||
if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
|
||||
AC_DEFCFG_MISC_NO_PRESENCE)
|
||||
return false;
|
||||
if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
|
||||
if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
|
||||
!codec->jackpoll_interval)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -39,6 +40,7 @@ EXPORT_SYMBOL_HDA(is_jack_detectable);
|
||||
static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
u32 pincap;
|
||||
u32 val;
|
||||
|
||||
if (!codec->no_trigger_sense) {
|
||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
||||
@@ -46,8 +48,11 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_SENSE, 0);
|
||||
}
|
||||
return snd_hda_codec_read(codec, nid, 0,
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
if (codec->inv_jack_detect)
|
||||
val ^= AC_PINSENSE_PRESENCE;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,9 +133,11 @@ 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_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val);
|
||||
#ifdef CONFIG_PM
|
||||
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val);
|
||||
int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val);
|
||||
void snd_hda_codec_resume_amp(struct hda_codec *codec);
|
||||
#endif
|
||||
|
||||
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int *tlv);
|
||||
@@ -383,6 +385,60 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
|
||||
int snd_hda_add_new_ctls(struct hda_codec *codec,
|
||||
const struct snd_kcontrol_new *knew);
|
||||
|
||||
/*
|
||||
* Fix-up pin default configurations and add default verbs
|
||||
*/
|
||||
|
||||
struct hda_pintbl {
|
||||
hda_nid_t nid;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
struct hda_model_fixup {
|
||||
const int id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct hda_fixup {
|
||||
int type;
|
||||
bool chained;
|
||||
int chain_id;
|
||||
union {
|
||||
const struct hda_pintbl *pins;
|
||||
const struct hda_verb *verbs;
|
||||
void (*func)(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action);
|
||||
} v;
|
||||
};
|
||||
|
||||
/* fixup types */
|
||||
enum {
|
||||
HDA_FIXUP_INVALID,
|
||||
HDA_FIXUP_PINS,
|
||||
HDA_FIXUP_VERBS,
|
||||
HDA_FIXUP_FUNC,
|
||||
HDA_FIXUP_PINCTLS,
|
||||
};
|
||||
|
||||
/* fixup action definitions */
|
||||
enum {
|
||||
HDA_FIXUP_ACT_PRE_PROBE,
|
||||
HDA_FIXUP_ACT_PROBE,
|
||||
HDA_FIXUP_ACT_INIT,
|
||||
HDA_FIXUP_ACT_BUILD,
|
||||
};
|
||||
|
||||
int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
|
||||
void snd_hda_apply_verbs(struct hda_codec *codec);
|
||||
void snd_hda_apply_pincfgs(struct hda_codec *codec,
|
||||
const struct hda_pintbl *cfg);
|
||||
void snd_hda_apply_fixup(struct hda_codec *codec, int action);
|
||||
void snd_hda_pick_fixup(struct hda_codec *codec,
|
||||
const struct hda_model_fixup *models,
|
||||
const struct snd_pci_quirk *quirk,
|
||||
const struct hda_fixup *fixlist);
|
||||
|
||||
/*
|
||||
* unsolicited event handler
|
||||
*/
|
||||
@@ -431,6 +487,8 @@ struct hda_bus_unsolicited {
|
||||
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
|
||||
|
||||
unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
|
||||
unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
|
||||
hda_nid_t pin, unsigned int val);
|
||||
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
|
||||
unsigned int val, bool cached);
|
||||
|
||||
@@ -470,6 +528,10 @@ snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
|
||||
return _snd_hda_set_pin_ctl(codec, pin, val, true);
|
||||
}
|
||||
|
||||
int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
|
||||
int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int val);
|
||||
|
||||
/*
|
||||
* get widget capabilities
|
||||
*/
|
||||
@@ -552,6 +614,7 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
|
||||
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
|
||||
int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
|
||||
#else
|
||||
static inline
|
||||
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
|
||||
@@ -564,6 +627,12 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static inline
|
||||
int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -596,7 +665,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
||||
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
|
||||
#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
|
||||
#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
|
||||
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
|
||||
#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
|
||||
#define get_amp_index(kc) get_amp_index_((kc)->private_value)
|
||||
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
|
||||
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
|
||||
|
||||
|
||||
@@ -138,16 +138,17 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
|
||||
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
|
||||
for (i = 0; i < indices; i++) {
|
||||
snd_iprintf(buffer, " [");
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_AMP_GAIN_MUTE,
|
||||
AC_AMP_GET_LEFT | dir | i);
|
||||
snd_iprintf(buffer, "0x%02x", val);
|
||||
if (stereo) {
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_AMP_GAIN_MUTE,
|
||||
AC_AMP_GET_LEFT | dir | i);
|
||||
snd_iprintf(buffer, "0x%02x ", val);
|
||||
AC_AMP_GET_RIGHT | dir | i);
|
||||
snd_iprintf(buffer, " 0x%02x", val);
|
||||
}
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_AMP_GAIN_MUTE,
|
||||
AC_AMP_GET_RIGHT | dir | i);
|
||||
snd_iprintf(buffer, "0x%02x]", val);
|
||||
snd_iprintf(buffer, "]");
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
+830
-636
File diff suppressed because it is too large
Load Diff
+16
-474
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
@@ -27,502 +26,46 @@
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_auto_parser.h"
|
||||
#include "hda_jack.h"
|
||||
#include "hda_generic.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;
|
||||
char input_labels[AUTO_PIN_LAST][32];
|
||||
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 const char * const 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, nid, 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, nid, 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 const char * const 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,
|
||||
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 const 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 const 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 const 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 const 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_set_pin_ctl(codec, pin, 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_set_pin_ctl(codec, pin, PIN_IN |
|
||||
snd_hda_get_default_vref(codec, pin));
|
||||
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 const struct hda_codec_ops ca0110_patch_ops = {
|
||||
.build_controls = ca0110_build_controls,
|
||||
.build_pcms = ca0110_build_pcms,
|
||||
.init = ca0110_init,
|
||||
.free = ca0110_free,
|
||||
.build_controls = snd_hda_gen_build_controls,
|
||||
.build_pcms = snd_hda_gen_build_pcms,
|
||||
.init = snd_hda_gen_init,
|
||||
.free = snd_hda_gen_free,
|
||||
.unsol_event = snd_hda_jack_unsol_event,
|
||||
};
|
||||
|
||||
|
||||
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 = get_wcaps_type(wcaps);
|
||||
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 < cfg->num_inputs; j++)
|
||||
if (cfg->inputs[j].pin == pin)
|
||||
break;
|
||||
if (j >= cfg->num_inputs)
|
||||
continue;
|
||||
spec->input_pins[n] = pin;
|
||||
snd_hda_get_pin_label(codec, pin, cfg,
|
||||
spec->input_labels[n],
|
||||
sizeof(spec->input_labels[n]), NULL);
|
||||
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 = spec->dig_out;
|
||||
}
|
||||
|
||||
static int ca0110_parse_auto_config(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
int err;
|
||||
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
|
||||
err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
parse_line_outs(codec);
|
||||
parse_hp_out(codec);
|
||||
parse_digital(codec);
|
||||
parse_input(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int patch_ca0110(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec;
|
||||
struct hda_gen_spec *spec;
|
||||
int err;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
snd_hda_gen_spec_init(spec);
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multi_cap_vol = 1;
|
||||
codec->bus->needs_damn_long_delay = 1;
|
||||
|
||||
err = ca0110_parse_auto_config(codec);
|
||||
@@ -534,8 +77,7 @@ static int patch_ca0110(struct hda_codec *codec)
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(codec->spec);
|
||||
codec->spec = NULL;
|
||||
snd_hda_gen_free(codec);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+101
-1199
File diff suppressed because it is too large
Load Diff
+47
-119
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
@@ -30,6 +29,9 @@
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_auto_parser.h"
|
||||
#include "hda_jack.h"
|
||||
#include "hda_generic.h"
|
||||
|
||||
#define NUM_PINS 11
|
||||
|
||||
|
||||
@@ -45,6 +47,10 @@ enum {
|
||||
};
|
||||
|
||||
struct cmi_spec {
|
||||
struct hda_gen_spec gen;
|
||||
|
||||
/* below are only for static models */
|
||||
|
||||
int board_config;
|
||||
unsigned int no_line_in: 1; /* no line-in (5-jack) */
|
||||
unsigned int front_panel: 1; /* has front-panel 2-jack */
|
||||
@@ -356,77 +362,6 @@ static int cmi9880_build_controls(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fill in the multi_dac_nids table, which will decide
|
||||
which audio widget to use for each channel */
|
||||
static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
|
||||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
hda_nid_t nid;
|
||||
int assigned[4];
|
||||
int i, j;
|
||||
|
||||
/* clear the table, only one c-media dac assumed here */
|
||||
memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
|
||||
memset(assigned, 0, sizeof(assigned));
|
||||
/* check the pins we found */
|
||||
for (i = 0; i < cfg->line_outs; i++) {
|
||||
nid = cfg->line_out_pins[i];
|
||||
/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
|
||||
if (nid >= 0x0b && nid <= 0x0e) {
|
||||
spec->dac_nids[i] = (nid - 0x0b) + 0x03;
|
||||
assigned[nid - 0x0b] = 1;
|
||||
}
|
||||
}
|
||||
/* left pin can be connect to any audio widget */
|
||||
for (i = 0; i < cfg->line_outs; i++) {
|
||||
nid = cfg->line_out_pins[i];
|
||||
if (nid <= 0x0e)
|
||||
continue;
|
||||
/* search for an empty channel */
|
||||
for (j = 0; j < cfg->line_outs; j++) {
|
||||
if (! assigned[j]) {
|
||||
spec->dac_nids[i] = j + 0x03;
|
||||
assigned[j] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spec->num_dacs = cfg->line_outs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create multi_init table, which is used for multichannel initialization */
|
||||
static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
|
||||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
hda_nid_t nid;
|
||||
int i, j, k;
|
||||
|
||||
/* clear the table, only one c-media dac assumed here */
|
||||
memset(spec->multi_init, 0, sizeof(spec->multi_init));
|
||||
for (j = 0, i = 0; i < cfg->line_outs; i++) {
|
||||
nid = cfg->line_out_pins[i];
|
||||
/* set as output */
|
||||
spec->multi_init[j].nid = nid;
|
||||
spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
|
||||
spec->multi_init[j].param = PIN_OUT;
|
||||
j++;
|
||||
if (nid > 0x0e) {
|
||||
/* set connection */
|
||||
spec->multi_init[j].nid = nid;
|
||||
spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
|
||||
spec->multi_init[j].param = 0;
|
||||
/* find the index in connect list */
|
||||
k = snd_hda_get_conn_index(codec, nid,
|
||||
spec->dac_nids[i], 0);
|
||||
if (k >= 0)
|
||||
spec->multi_init[j].param = k;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmi9880_init(struct hda_codec *codec)
|
||||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
@@ -632,6 +567,36 @@ static const struct hda_codec_ops cmi9880_patch_ops = {
|
||||
.free = cmi9880_free,
|
||||
};
|
||||
|
||||
/*
|
||||
* stuff for auto-parser
|
||||
*/
|
||||
static const struct hda_codec_ops cmi_auto_patch_ops = {
|
||||
.build_controls = snd_hda_gen_build_controls,
|
||||
.build_pcms = snd_hda_gen_build_pcms,
|
||||
.init = snd_hda_gen_init,
|
||||
.free = snd_hda_gen_free,
|
||||
.unsol_event = snd_hda_jack_unsol_event,
|
||||
};
|
||||
|
||||
static int cmi_parse_auto_config(struct hda_codec *codec)
|
||||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->gen.autocfg;
|
||||
int err;
|
||||
|
||||
snd_hda_gen_spec_init(&spec->gen);
|
||||
|
||||
err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_gen_parse_auto_config(codec, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
codec->patch_ops = cmi_auto_patch_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_cmi9880(struct hda_codec *codec)
|
||||
{
|
||||
struct cmi_spec *spec;
|
||||
@@ -650,6 +615,15 @@ static int patch_cmi9880(struct hda_codec *codec)
|
||||
spec->board_config = CMI_AUTO; /* try everything */
|
||||
}
|
||||
|
||||
if (spec->board_config == CMI_AUTO) {
|
||||
int err = cmi_parse_auto_config(codec);
|
||||
if (err < 0) {
|
||||
snd_hda_gen_free(codec);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy default DAC NIDs */
|
||||
memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
|
||||
spec->num_dacs = 4;
|
||||
@@ -678,59 +652,13 @@ static int patch_cmi9880(struct hda_codec *codec)
|
||||
}
|
||||
break;
|
||||
case CMI_ALLOUT:
|
||||
default:
|
||||
spec->front_panel = 1;
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->no_line_in = 1;
|
||||
spec->input_mux = &cmi9880_no_line_mux;
|
||||
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
|
||||
break;
|
||||
case CMI_AUTO:
|
||||
{
|
||||
unsigned int port_e, port_f, port_g, port_h;
|
||||
unsigned int port_spdifi, port_spdifo;
|
||||
struct auto_pin_cfg cfg;
|
||||
|
||||
/* collect pin default configuration */
|
||||
port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
|
||||
port_f = snd_hda_codec_get_pincfg(codec, 0x10);
|
||||
spec->front_panel = 1;
|
||||
if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
|
||||
get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
|
||||
port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
|
||||
port_h = snd_hda_codec_get_pincfg(codec, 0x20);
|
||||
spec->channel_modes = cmi9880_channel_modes;
|
||||
/* no front panel */
|
||||
if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
|
||||
get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
|
||||
/* no optional rear panel */
|
||||
spec->board_config = CMI_MINIMAL;
|
||||
spec->front_panel = 0;
|
||||
spec->num_channel_modes = 2;
|
||||
} else {
|
||||
spec->board_config = CMI_MIN_FP;
|
||||
spec->num_channel_modes = 3;
|
||||
}
|
||||
spec->input_mux = &cmi9880_basic_mux;
|
||||
spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
|
||||
} else {
|
||||
spec->input_mux = &cmi9880_basic_mux;
|
||||
port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
|
||||
port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
|
||||
if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
|
||||
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
|
||||
if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
|
||||
spec->dig_in_nid = CMI_DIG_IN_NID;
|
||||
spec->multiout.max_channels = 8;
|
||||
}
|
||||
snd_hda_parse_pin_def_config(codec, &cfg, NULL);
|
||||
if (cfg.line_outs) {
|
||||
spec->multiout.max_channels = cfg.line_outs * 2;
|
||||
cmi9880_fill_multi_dac_nids(codec, &cfg);
|
||||
cmi9880_fill_multi_init(codec, &cfg);
|
||||
} else
|
||||
snd_printd("patch_cmedia: cannot detect association in defcfg\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spec->multiout.num_dacs = spec->num_dacs;
|
||||
|
||||
+103
-1278
File diff suppressed because it is too large
Load Diff
+502
-3822
File diff suppressed because it is too large
Load Diff
+2647
-4842
File diff suppressed because it is too large
Load Diff
+209
-2412
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user