?? patch_via.c
字號:
/* * Universal Interface for Intel High Definition Audio Codec * * HD audio interface patch for VIA VT1708 codec * * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com> * Takashi Iwai <tiwai@suse.de> * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * *//* *//* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec *//* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid *//* 2006-08-02 Lydia Wang Add support to VT1709 codec *//* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug *//* *//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */#include <sound/driver.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/slab.h>#include <sound/core.h>#include "hda_codec.h"#include "hda_local.h"/* amp values */#define AMP_VAL_IDX_SHIFT 19#define AMP_VAL_IDX_MASK (0x0f<<19)#define NUM_CONTROL_ALLOC 32#define NUM_VERB_ALLOC 32/* Pin Widget NID */#define VT1708_HP_NID 0x13#define VT1708_DIGOUT_NID 0x14#define VT1708_DIGIN_NID 0x16#define VT1709_HP_DAC_NID 0x28#define VT1709_DIGOUT_NID 0x13#define VT1709_DIGIN_NID 0x17#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE,};enum { AUTO_SEQ_FRONT, AUTO_SEQ_SURROUND, AUTO_SEQ_CENLFE, AUTO_SEQ_SIDE};static struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0),};struct via_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[3]; unsigned int num_mixers; struct hda_verb *init_verbs; char *stream_name_analog; struct hda_pcm_stream *stream_analog_playback; struct hda_pcm_stream *stream_analog_capture; char *stream_name_digital; struct hda_pcm_stream *stream_digital_playback; struct hda_pcm_stream *stream_digital_capture; /* playback */ struct hda_multi_out multiout; /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; hda_nid_t dig_in_nid; /* capture source */ const struct hda_input_mux *input_mux; unsigned int cur_mux[3]; /* PCM information */ struct hda_pcm pcm_rec[2]; /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; unsigned int num_kctl_alloc, num_kctl_used; struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];#ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback;#endif};static hda_nid_t vt1708_adc_nids[2] = { /* ADC1-2 */ 0x15, 0x27};static hda_nid_t vt1709_adc_nids[3] = { /* ADC1-2 */ 0x14, 0x15, 0x16};/* add dynamic controls */static int via_add_control(struct via_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; /* array + terminator */ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); 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 = vt1708_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; knew->private_value = val; spec->num_kctl_used++; return 0;}/* create input playback/capture controls for the given pin */static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, const char *ctlname, int idx, int mix_nid){ char name[32]; int err; sprintf(name, "%s Playback Volume", ctlname); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; return 0;}static void via_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx){ /* set as output */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);}static void via_auto_init_multi_out(struct hda_codec *codec){ struct via_spec *spec = codec->spec; int i; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); }}static void via_auto_init_hp_out(struct hda_codec *codec){ struct via_spec *spec = codec->spec; hda_nid_t pin; pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front */ via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);}static void via_auto_init_analog_input(struct hda_codec *codec){ struct via_spec *spec = codec->spec; int i; for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, (i <= AUTO_PIN_FRONT_MIC ? PIN_VREF50 : PIN_IN)); }}/* * input MUX handling */static int via_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; return snd_hda_input_mux_info(spec->input_mux, uinfo);}static int via_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; return 0;}static int via_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int vendor_id = codec->vendor_id; /* AIW0 lydia 060801 add for correct sw0 input select */ if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0)) return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x18, &spec->cur_mux[adc_idx]); else if ((IS_VT1709_10CH_VENDORID(vendor_id) || IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) ) return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x19, &spec->cur_mux[adc_idx]); else return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);}/* capture mixer elements */static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", .count = 1, .info = via_mux_enum_info, .get = via_mux_enum_get, .put = via_mux_enum_put, }, { } /* end */};/* * generic initialization of ADC, input mixers and output mixers */static struct hda_verb vt1708_volume_init_verbs[] = { /* * Unmute ADC0-1 and set the default input to mic-in */ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x19 - 0x1b) */ /* set vol=0 to output mixers */ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* Setup default input to PW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, /* Set mic as default input of sw0 */ {0x18, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},};static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ struct via_spec *spec = codec->spec; return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);}static int via_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 via_spec *spec = codec->spec; return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);}static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ struct via_spec *spec = codec->spec; return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);}/* * Digital out */static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ struct via_spec *spec = codec->spec; return snd_hda_multi_out_dig_open(codec, &spec->multiout);}static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ struct via_spec *spec = codec->spec; return snd_hda_multi_out_dig_close(codec, &spec->multiout);}static int via_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 via_spec *spec = codec->spec; return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream);}/* * Analog capture */static int via_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 via_spec *spec = codec->spec; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], stream_tag, 0, format); return 0;}static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream){ struct via_spec *spec = codec->spec; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); return 0;}static struct hda_pcm_stream vt1708_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, .prepare = via_playback_pcm_prepare, .cleanup = via_playback_pcm_cleanup },};static struct hda_pcm_stream vt1708_pcm_analog_capture = { .substreams = 2, .channels_min = 2, .channels_max = 2, .nid = 0x15, /* NID to query formats and rates */ .ops = { .prepare = via_capture_pcm_prepare, .cleanup = via_capture_pcm_cleanup },};static struct hda_pcm_stream vt1708_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, .close = via_dig_playback_pcm_close, .prepare = via_dig_playback_pcm_prepare },};static struct hda_pcm_stream vt1708_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2,};static int via_build_controls(struct hda_codec *codec){ struct via_spec *spec = codec->spec; int err; int i; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) return err; } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -