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

📄 hda_generic.c

📁 一个Linux下的软猫驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
	unsigned int pinctl;	char *label;	const char *type;	if (node->checked)		return 0;	node->checked = 1;	if (node->type != AC_WID_PIN) {		for (i = 0; i < node->nconns; i++) {			struct hda_gnode *child;			child = hda_get_node(spec, node->conn_list[i]);			if (! child)				continue;			err = parse_adc_sub_nodes(codec, spec, child);			if (err < 0)				return err;			if (err > 0) {				/* found one,				 * select the path, unmute both input and output				 */				if (node->nconns > 1)					select_input_connection(codec, node, i);				unmute_input(codec, node, i);				unmute_output(codec, node);				return err;			}		}		return 0;	}	/* input capable? */	if (! (node->pin_caps & AC_PINCAP_IN))		return 0;	if (node->wid_caps & AC_WCAP_DIGITAL)		return 0; /* skip SPDIF */	if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {		snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");		return -EINVAL;	}	pinctl = AC_PINCTL_IN_EN;	/* create a proper capture source label */	type = get_input_type(node, &pinctl);	if (! type) {		/* input as default? */		if (! (node->pin_ctl & AC_PINCTL_IN_EN))			return 0;		type = "Input";	}	label = spec->cap_labels[spec->input_mux.num_items];	strcpy(label, type);	spec->input_mux.items[spec->input_mux.num_items].label = label;	/* unmute the PIN external input */	unmute_input(codec, node, 0); /* index = 0? */	/* set PIN-In enable */	snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);	return 1; /* found */}/* * parse input */static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node){	struct hda_gspec *spec = codec->spec;	struct hda_gnode *node;	int i, err;	snd_printdd("AUD_IN = %x\n", adc_node->nid);	clear_check_flags(spec);	// awk added - fixed no recording due to muted widget	unmute_input(codec, adc_node, 0);		/*	 * check each connection of the ADC	 * if it reaches to a proper input PIN, add the path as the	 * input path.	 */	for (i = 0; i < adc_node->nconns; i++) {		node = hda_get_node(spec, adc_node->conn_list[i]);		if (! node)			continue;		err = parse_adc_sub_nodes(codec, spec, node);		if (err < 0)			return err;		else if (err > 0) {			struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items];			char *buf = spec->cap_labels[spec->input_mux.num_items];			int ocap;			for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) {				if (! strcmp(buf, spec->cap_labels[ocap])) {					/* same label already exists,					 * put the index number to be unique					 */					sprintf(buf, "%s %d", spec->cap_labels[ocap],						spec->input_mux.num_items);				}			}			csrc->index = i;			spec->input_mux.num_items++;		}	}	if (! spec->input_mux.num_items)		return 0; /* no input path found... */	snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);	for (i = 0; i < spec->input_mux.num_items; i++)		snd_printdd("  [%s] IDX=0x%x\n", spec->input_mux.items[i].label,			    spec->input_mux.items[i].index);	spec->adc_node = adc_node;	return 1;}/* * parse input */static int parse_input(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	struct list_head *p;	struct hda_gnode *node;	int err;	/*	 * At first we look for an audio input widget.	 * If it reaches to certain input PINs, we take it as the	 * input path.	 */	list_for_each(p, &spec->nid_list) {		node = list_entry(p, struct hda_gnode, list);		if (node->wid_caps & AC_WCAP_DIGITAL)			continue; /* skip SPDIF */		if (node->type == AC_WID_AUD_IN) {			err = parse_input_path(codec, node);			if (err < 0)				return err;			else if (err > 0)				return 0;		}	}	snd_printd("hda_generic: no proper input path found\n");	return 0;}/* * create mixer controls if possible */#define DIR_OUT		0x1#define DIR_IN		0x2static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,			unsigned int index, const char *type, const char *dir_sfx){	char name[32];	int err;	int created = 0;	snd_kcontrol_new_t knew;	if (type)		sprintf(name, "%s %s Switch", type, dir_sfx);	else		sprintf(name, "%s Switch", dir_sfx);	if ((node->wid_caps & AC_WCAP_IN_AMP) &&	    (node->amp_in_caps & AC_AMPCAP_MUTE)) {		knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)			return err;		created = 1;	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&		   (node->amp_out_caps & AC_AMPCAP_MUTE)) {		knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)			return err;		created = 1;	}	if (type)		sprintf(name, "%s %s Volume", type, dir_sfx);	else		sprintf(name, "%s Volume", dir_sfx);	if ((node->wid_caps & AC_WCAP_IN_AMP) &&	    (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {		knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)			return err;		created = 1;	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&		   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {		knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)			return err;		created = 1;	}	return created;}/* * check whether the controls with the given name and direction suffix already exist */static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir){	snd_ctl_elem_id_t id;	memset(&id, 0, sizeof(id));	sprintf(id.name, "%s %s Volume", type, dir);	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;	if (snd_ctl_find_id(codec->bus->card, &id))		return 1;	sprintf(id.name, "%s %s Switch", type, dir);	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;	if (snd_ctl_find_id(codec->bus->card, &id))		return 1;	return 0;}/* * build output mixer controls */static int build_output_controls(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	int err;	err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index,			   "PCM", "Playback");	if (err < 0)		return err;	return 0;}/* create capture volume/switch */static int build_input_controls(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	struct hda_gnode *adc_node = spec->adc_node;	int err;	if (! adc_node)		return 0; /* not found */	/* create capture volume and switch controls if the ADC has an amp */	err = create_mixer(codec, adc_node, 0, NULL, "Capture");	/* create input MUX if multiple sources are available */	if (spec->input_mux.num_items > 1) {		static snd_kcontrol_new_t cap_sel = {			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,			.name = "Capture Source",			.info = capture_source_info,			.get = capture_source_get,			.put = capture_source_put,		};		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0)			return err;		spec->cur_cap_src = 0;		select_input_connection(codec, adc_node, spec->input_mux.items[0].index);	}	return 0;}/* * parse the nodes recursively until reach to the output PIN. * * returns 0 - if not found, *         1 - if found, but no mixer is created *         2 - if found and mixer was already created, (just skip) *         a negative error code */static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,			       struct hda_gnode *node, struct hda_gnode *dest_node,			       const char *type){	int i, err;	if (node->checked)		return 0;	node->checked = 1;	if (node == dest_node) {		/* loopback connection found */		return 1;	}	for (i = 0; i < node->nconns; i++) {		struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);		if (! child)			continue;		err = parse_loopback_path(codec, spec, child, dest_node, type);		if (err < 0)			return err;		else if (err >= 1) {			if (err == 1) {				err = create_mixer(codec, node, i, type, "Playback");				if (err < 0)					return err;				if (err > 0)					return 2; /* ok, created */				/* not created, maybe in the lower path */				err = 1;			}			/* connect and unmute */			if (node->nconns > 1)				select_input_connection(codec, node, i);			unmute_input(codec, node, i);			unmute_output(codec, node);			return err;		}	}	return 0;}/* * parse the tree and build the loopback controls */static int build_loopback_controls(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	struct list_head *p;	struct hda_gnode *node;	int err;	const char *type;	if (! spec->out_pin_node)		return 0;	list_for_each(p, &spec->nid_list) {		node = list_entry(p, struct hda_gnode, list);		if (node->type != AC_WID_PIN)			continue;		/* input capable? */		if (! (node->pin_caps & AC_PINCAP_IN))			return 0;		type = get_input_type(node, NULL);		if (type) {			if (check_existing_control(codec, type, "Playback"))				continue;			clear_check_flags(spec);			err = parse_loopback_path(codec, spec, spec->out_pin_node,						  node, type);			if (err < 0)				return err;			if (! err)				continue;		}	}	return 0;}/* * build mixer controls */static int build_generic_controls(struct hda_codec *codec){	int err;	if ((err = build_input_controls(codec)) < 0 ||	    (err = build_output_controls(codec)) < 0 ||	    (err = build_loopback_controls(codec)) < 0)		return err;	return 0;}/* * PCM */static struct hda_pcm_stream generic_pcm_playback = {	.substreams = 1,	.channels_min = 2,	.channels_max = 2,};static int build_generic_pcms(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	struct hda_pcm *info = &spec->pcm_rec;	if (! spec->dac_node && ! spec->adc_node) {		snd_printd("hda_generic: no PCM found\n");		return 0;	}	codec->num_pcms = 1;	codec->pcm_info = info;	info->name = "HDA Generic";	if (spec->dac_node) {		info->stream[0] = generic_pcm_playback;		info->stream[0].nid = spec->dac_node->nid;	}	if (spec->adc_node) {		info->stream[1] = generic_pcm_playback;		info->stream[1].nid = spec->adc_node->nid;	}	return 0;}/* */static struct hda_codec_ops generic_patch_ops = {	.build_controls = build_generic_controls,	.build_pcms = build_generic_pcms,	.free = snd_hda_generic_free,};/* * the generic parser */int snd_hda_parse_generic_codec(struct hda_codec *codec){	struct hda_gspec *spec;	int err;	if(!codec->afg)		return 0;	spec = kzalloc(sizeof(*spec), GFP_KERNEL);	if (spec == NULL) {		printk(KERN_ERR "hda_generic: can't allocate spec\n");		return -ENOMEM;	}	codec->spec = spec;	INIT_LIST_HEAD(&spec->nid_list);	if ((err = build_afg_tree(codec)) < 0)		goto error;	if ((err = parse_input(codec)) < 0 ||	    (err = parse_output(codec)) < 0)		goto error;	codec->patch_ops = generic_patch_ops;	return 0; error:	snd_hda_generic_free(codec);	return err;}

⌨️ 快捷键说明

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