📄 patch_sigmatel.c
字号:
/* NID is set in stac92xx_build_pcms */ .ops = { .open = stac92xx_dig_playback_pcm_open, .close = stac92xx_dig_playback_pcm_close, .prepare = stac92xx_dig_playback_pcm_prepare },};static struct hda_pcm_stream stac92xx_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in stac92xx_build_pcms */};static struct hda_pcm_stream stac92xx_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, .nid = 0x02, /* NID to query formats and rates */ .ops = { .open = stac92xx_playback_pcm_open, .prepare = stac92xx_playback_pcm_prepare, .cleanup = stac92xx_playback_pcm_cleanup },};static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, .nid = 0x06, /* NID to query formats and rates */ .ops = { .open = stac92xx_playback_pcm_open, .prepare = stac92xx_playback_pcm_prepare, .cleanup = stac92xx_playback_pcm_cleanup },};static struct hda_pcm_stream stac92xx_pcm_analog_capture = { .channels_min = 2, .channels_max = 2, /* NID + .substreams is set in stac92xx_build_pcms */ .ops = { .prepare = stac92xx_capture_pcm_prepare, .cleanup = stac92xx_capture_pcm_cleanup },};static int stac92xx_build_pcms(struct hda_codec *codec){ struct sigmatel_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; codec->pcm_info = info; info->name = "STAC92xx Analog"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs; if (spec->alt_switch) { codec->num_pcms++; info++; info->name = "STAC92xx Analog Alt"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback; } if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; info++; info->name = "STAC92xx Digital"; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } if (spec->dig_in_nid) { info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } } return 0;}static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid){ unsigned int pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; if (pincap & AC_PINCAP_VREF_100) return AC_PINCTL_VREF_100; if (pincap & AC_PINCAP_VREF_80) return AC_PINCTL_VREF_80; if (pincap & AC_PINCAP_VREF_50) return AC_PINCTL_VREF_50; if (pincap & AC_PINCAP_VREF_GRD) return AC_PINCTL_VREF_GRD; return 0;}static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type){ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);}#define stac92xx_io_switch_info snd_ctl_boolean_mono_infostatic int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; int io_idx = kcontrol-> private_value & 0xff; ucontrol->value.integer.value[0] = spec->io_switch[io_idx]; return 0;}static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value >> 8; int io_idx = kcontrol-> private_value & 0xff; unsigned short val = ucontrol->value.integer.value[0]; spec->io_switch[io_idx] = val; if (val) stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); else { unsigned int pinctl = AC_PINCTL_IN_EN; if (io_idx) /* set VREF for mic */ pinctl |= stac92xx_get_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } return 1;}#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_infostatic int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; ucontrol->value.integer.value[0] = spec->clfe_swap; return 0;}static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value & 0xff; if (spec->clfe_swap == ucontrol->value.integer.value[0]) return 0; spec->clfe_swap = ucontrol->value.integer.value[0]; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, spec->clfe_swap ? 0x4 : 0x0); return 1;}#define STAC_CODEC_IO_SWITCH(xname, xpval) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = 0, \ .info = stac92xx_io_switch_info, \ .get = stac92xx_io_switch_get, \ .put = stac92xx_io_switch_put, \ .private_value = xpval, \ }#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = 0, \ .info = stac92xx_clfe_switch_info, \ .get = stac92xx_clfe_switch_get, \ .put = stac92xx_clfe_switch_put, \ .private_value = xpval, \ }enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH};static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0),};/* add dynamic controls */static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val){ struct snd_kcontrol_new *knew; if (spec->num_kctl_used >= spec->num_kctl_alloc) { int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ if (! knew) return -ENOMEM; if (spec->kctl_alloc) { memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); kfree(spec->kctl_alloc); } spec->kctl_alloc = knew; spec->num_kctl_alloc = num; } knew = &spec->kctl_alloc[spec->num_kctl_used]; *knew = stac92xx_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; knew->private_value = val; spec->num_kctl_used++; return 0;}/* flag inputs as additional dynamic lineouts */static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg){ struct sigmatel_spec *spec = codec->spec; unsigned int wcaps, wtype; int i, num_dacs = 0; /* use the wcaps cache to count all DACs available for line-outs */ for (i = 0; i < codec->num_nodes; i++) { wcaps = codec->wcaps[i]; wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) num_dacs++; } snd_printdd("%s: total dac count=%d\n", __func__, num_dacs); switch (cfg->line_outs) { case 3: /* add line-in as side */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) { cfg->line_out_pins[cfg->line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; cfg->line_outs++; } break; case 2: /* add line-in as clfe and mic as side */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) { cfg->line_out_pins[cfg->line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; cfg->line_outs++; } if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) { cfg->line_out_pins[cfg->line_outs] = cfg->input_pins[AUTO_PIN_MIC]; spec->mic_switch = 1; cfg->line_outs++; } break; case 1: /* add line-in as surr and mic as clfe */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) { cfg->line_out_pins[cfg->line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; cfg->line_outs++; } if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) { cfg->line_out_pins[cfg->line_outs] = cfg->input_pins[AUTO_PIN_MIC]; spec->mic_switch = 1; cfg->line_outs++; } break; } return 0;}static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid){ int i; for (i = 0; i < spec->multiout.num_dacs; i++) { if (spec->multiout.dac_nids[i] == nid) return 1; } return 0;}/* * Fill in the dac_nids table from the parsed pin configuration * This function only works when every pin in line_out_pins[] * contains atleast one DAC in its connection list. Some 92xx * codecs are not connected directly to a DAC, such as the 9200 * and 9202/925x. For those, dac_nids[] must be hard-coded. */static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, struct auto_pin_cfg *cfg){ struct sigmatel_spec *spec = codec->spec; int i, j, conn_len = 0; hda_nid_t nid, conn[HDA_MAX_CONNECTIONS]; unsigned int wcaps, wtype; for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); for (j = 0; j < conn_len; j++) { wcaps = snd_hda_param_read(codec, conn[j], AC_PAR_AUDIO_WIDGET_CAP); wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) continue; /* conn[j] is a DAC routed to this line-out */ if (!is_in_dac_nids(spec, conn[j])) break; } if (j == conn_len) { if (spec->multiout.num_dacs > 0) { /* we have already working output pins, * so let's drop the broken ones again */ cfg->line_outs = spec->multiout.num_dacs; break; } /* error out, no available DAC found */ snd_printk(KERN_ERR "%s: No available DAC for pin 0x%x\n", __func__, nid); return -ENODEV; } spec->multiout.dac_nids[i] = conn[j]; spec->multiout.num_dacs++; if (conn_len > 1) { /* select this DAC in the pin's input mux */ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, j); } } snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", spec->multiout.num_dacs, spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], spec->multiout.dac_nids[3], spec->multiout.dac_nids[4]); return 0;}/* create volume control/switch for the given prefx type */static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs){ char name[32]; int err; sprintf(name, "%s Playback Volume", pfx); err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", pfx); err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; return 0;}/* add playback controls from the parsed DAC table */static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg){ static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; hda_nid_t nid; int i, err; struct sigmatel_spec *spec = codec->spec; unsigned int wid_caps; for (i = 0; i < cfg->line_outs; i++) { if (!spec->multiout.dac_nids[i]) continue; nid = spec->multiout.dac_nids[i]; if (i == 2) { /* Center/LFE */ err = create_controls(spec, "Center", nid, 1); if (err < 0) return err; err = create_controls(spec, "LFE", nid, 2); if (err < 0) return err; wid_caps = get_wcaps(codec, nid); if (wid_caps & AC_WCAP_LR_SWAP) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_CLFE_SWITCH, "Swap Center/LFE Playback Switch", nid); if (err < 0) return err; } } else { err = create_controls(spec, chname[i], nid, 3); if (err < 0) return err; } } if (spec->line_switch) if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0) return err; if (spec->mic_switch) if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0) return err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -