diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 233 |
1 files changed, 230 insertions, 3 deletions
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index a44d64e828d..e64e29dac82 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -29,6 +29,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#define NUM_PINS 11 /* board config type */ @@ -38,6 +39,7 @@ enum { CMI_FULL, /* back 6-jack + front-panel 2-jack */ CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */ CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */ + CMI_AUTO, /* let driver guess it */ }; struct cmi_spec { @@ -48,6 +50,7 @@ struct cmi_spec { /* playback */ struct hda_multi_out multiout; + hda_nid_t dac_nids[4]; /* NID for each DAC */ /* capture */ hda_nid_t *adc_nids; @@ -63,6 +66,15 @@ struct cmi_spec { const struct cmi_channel_mode *channel_modes; struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* pin deafault configuration */ + hda_nid_t pin_nid[NUM_PINS]; + unsigned int def_conf[NUM_PINS]; + unsigned int pin_def_confs; + + /* multichannel pins */ + hda_nid_t multich_pin[4]; /* max 8-channel */ + struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */ }; /* @@ -353,6 +365,175 @@ static int cmi9880_build_controls(struct hda_codec *codec) return 0; } +#define AC_DEFCFG_ASSOC_SHIFT 4 +#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) +#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) +#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE) + +/* get all pin default configuration in def_conf */ +static int cmi9880_get_pin_def_config(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid, nid_start; + int i = 0, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); + for (nid = nid_start; nid < nodes + nid_start; nid++) { + unsigned int wid_caps = snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + /* read all default configuration for pin complex */ + if (wid_type == AC_WID_PIN) { + spec->pin_nid[i] = nid; + spec->def_conf[i] = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + i++; + } + } + spec->pin_def_confs = i; + return 0; +} + +/* get a pin default configuration of nid in def_conf */ +static unsigned int cmi9880_get_def_config(struct hda_codec *codec, hda_nid_t nid) +{ + struct cmi_spec *spec = codec->spec; + int i = 0; + + while (spec->pin_nid[i] != nid && i < spec->pin_def_confs) + i++; + if (i == spec->pin_def_confs) + return (unsigned int) -1; + else + return spec->def_conf[i]; +} + +/* decide what pins to use for multichannel playback */ +static int cmi9880_get_multich_pins(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + int i, j, pins, seq[4]; + int max_channel = 0; + unsigned int def_conf, sequence; + hda_nid_t nid; + + memset(spec->multich_pin, 0, sizeof(spec->multich_pin)); + for (pins = 0, i = 0; i < spec->pin_def_confs && pins < 4; i++) { + def_conf = spec->def_conf[i]; + /* skip pin not connected */ + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + /* get the sequence if association == 1 */ + /* the other pins have association = 0, incorrect in spec 1.0 */ + if (get_defcfg_association(def_conf) == 1) { + sequence = get_defcfg_sequence(def_conf); + seq[pins] = sequence; + spec->multich_pin[pins] = spec->pin_nid[i]; + pins++; // ready for next slot + max_channel += 2; + } + } + /* sort by sequence, data collected here will be for Windows */ + for (i = 0; i < pins; i++) { + for (j = i + 1; j < pins; j++) { + if (seq[j] < seq[i]) { + sequence = seq[j]; + nid = spec->multich_pin[j]; + seq[j] = seq[i]; + spec->multich_pin[j] = spec->multich_pin[i]; + seq[i] = sequence; + spec->multich_pin[i] = nid; + } + } + } + /* the pin assignment is for front, C/LFE, surround and back */ + if (max_channel >= 6) { + hda_nid_t temp; + /* exchange pin of C/LFE and surround */ + temp = spec->multich_pin[1]; + spec->multich_pin[1] = spec->multich_pin[2]; + spec->multich_pin[2] = temp; + } + return max_channel; +} + +/* 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) +{ + 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 < spec->multiout.max_channels / 2; i++) { + nid = spec->multich_pin[i]; + /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ + if (nid <= 0x0e && nid >= 0x0b) { + spec->dac_nids[i] = nid - 0x08; + assigned[nid - 0x0b] = 1; + } + } + /* left pin can be connect to any audio widget */ + for (i = 0; i < spec->multiout.max_channels / 2; i++) { + if (!assigned[i]) { + /* search for an empty channel */ + /* I should also check the pin type */ + for (j = 0; j < ARRAY_SIZE(spec->dac_nids); j++) + if (! spec->dac_nids[j]) { + spec->dac_nids[j] = i + 3; + assigned[i] = 1; + break; + } + } + } + return 0; +} + +/* create multi_init table, which is used for multichannel initialization */ +static int cmi9880_fill_multi_init(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid; + int i, j, k, len; + + /* 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 < spec->multiout.max_channels / 2; i++) { + hda_nid_t conn[4]; + nid = spec->multich_pin[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 = 0xc0; + j++; + /* nid 0x0f,0x10,0x1f,0x20 are needed to set connection */ + switch (nid) { + case 0x0f: + case 0x10: + case 0x1f: + case 0x20: + /* set connection */ + spec->multi_init[j].nid = nid; + spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; + /* find the index in connect list */ + len = snd_hda_get_connections(codec, nid, conn, 4); + for (k = 0; k < len; k++) + if (conn[k] == spec->dac_nids[i]) + break; + spec->multi_init[j].param = k < len ? k : 0; + j++; + break; + } + } + return 0; +} + static int cmi9880_init(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; @@ -360,6 +541,8 @@ static int cmi9880_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cmi9880_allout_init); else snd_hda_sequence_write(codec, cmi9880_basic_init); + if (spec->board_config == CMI_AUTO) + snd_hda_sequence_write(codec, spec->multi_init); return 0; } @@ -546,6 +729,7 @@ static struct hda_board_config cmi9880_cfg_tbl[] = { { .modelname = "full", .config = CMI_FULL }, { .modelname = "full_dig", .config = CMI_FULL_DIG }, { .modelname = "allout", .config = CMI_ALLOUT }, + { .modelname = "auto", .config = CMI_AUTO }, {} /* terminator */ }; @@ -570,10 +754,13 @@ static int patch_cmi9880(struct hda_codec *codec) codec->spec = spec; spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl); if (spec->board_config < 0) { - snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); - spec->board_config = CMI_FULL_DIG; /* try everything */ + snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); + spec->board_config = CMI_AUTO; /* try everything */ } + /* copy default DAC NIDs */ + memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); + switch (spec->board_config) { case CMI_MINIMAL: case CMI_MIN_FP: @@ -605,10 +792,50 @@ static int patch_cmi9880(struct hda_codec *codec) 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; + /* collect pin default configuration */ + cmi9880_get_pin_def_config(codec); + port_e = cmi9880_get_def_config(codec, 0x0f); + port_f = cmi9880_get_def_config(codec, 0x10); + port_g = cmi9880_get_def_config(codec, 0x1f); + port_h = cmi9880_get_def_config(codec, 0x20); + port_spdifi = cmi9880_get_def_config(codec, 0x13); + port_spdifo = cmi9880_get_def_config(codec, 0x12); + spec->front_panel = 1; + if ((get_defcfg_connect(port_e) == AC_JACK_PORT_NONE) + || (get_defcfg_connect(port_f) == AC_JACK_PORT_NONE)) { + spec->surr_switch = 1; + /* 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_ch_modes = 2; + } else + spec->board_config = CMI_MIN_FP; + spec->num_ch_modes = 3; + spec->channel_modes = cmi9880_channel_modes; + spec->input_mux = &cmi9880_basic_mux; + } else { + spec->input_mux = &cmi9880_basic_mux; + if (get_defcfg_connect(port_spdifo) != 1) + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + if (get_defcfg_connect(port_spdifi) != 1) + spec->dig_in_nid = CMI_DIG_IN_NID; + } + spec->multiout.max_channels = cmi9880_get_multich_pins(codec); + cmi9880_fill_multi_dac_nids(codec); + cmi9880_fill_multi_init(codec); + } + break; } spec->multiout.num_dacs = 4; - spec->multiout.dac_nids = cmi9880_dac_nids; + spec->multiout.dac_nids = spec->dac_nids; spec->adc_nids = cmi9880_adc_nids; |