⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 patch_via.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -