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

📄 hda_generic.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
		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_cache(codec, node->nid, 0,				  AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);	return 1; /* found */}/* add a capture source element */static void add_cap_src(struct hda_gspec *spec, int idx){	struct hda_input_mux_item *csrc;	char *buf;	int num, ocap;	num = spec->input_mux.num_items;	csrc = &spec->input_mux.items[num];	buf = spec->cap_labels[num];	for (ocap = 0; ocap < num; 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], num);			break;		}	}	csrc->index = idx;	spec->input_mux.num_items++;}/* * 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.	 */	/* first, check the direct connections to PIN widgets */	for (i = 0; i < adc_node->nconns; i++) {		node = hda_get_node(spec, adc_node->conn_list[i]);		if (node && node->type == AC_WID_PIN) {			err = parse_adc_sub_nodes(codec, spec, node);			if (err < 0)				return err;			else if (err > 0)				add_cap_src(spec, i);		}	}	/* ... then check the rests, more complicated connections */	for (i = 0; i < adc_node->nconns; i++) {		node = hda_get_node(spec, adc_node->conn_list[i]);		if (node && node->type != AC_WID_PIN) {			err = parse_adc_sub_nodes(codec, spec, node);			if (err < 0)				return err;			else if (err > 0)				add_cap_src(spec, i);		}	}	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 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_entry(node, &spec->nid_list, 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;}#ifdef CONFIG_SND_HDA_POWER_SAVEstatic void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,			       int dir, int idx){	struct hda_gspec *spec = codec->spec;	struct hda_amp_list *p;	if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {		snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");		return;	}	p = &spec->loopback_list[spec->num_loopbacks++];	p->nid = nid;	p->dir = dir;	p->idx = idx;	spec->loopback.amplist = spec->loopback_list;}#else#define add_input_loopback(codec,nid,dir,idx)#endif/* * create mixer controls if possible */static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,			unsigned int index, const char *type,			const char *dir_sfx, int is_loopback){	char name[32];	int err;	int created = 0;	struct snd_kcontrol_new 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 = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);		if (is_loopback)			add_input_loopback(codec, node->nid, HDA_INPUT, index);		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 = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);		if (is_loopback)			add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);		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 = (struct snd_kcontrol_new)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 = (struct snd_kcontrol_new)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){	struct snd_ctl_elem_id 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 create_output_mixers(struct hda_codec *codec, const char **names){	struct hda_gspec *spec = codec->spec;	int i, err;	for (i = 0; i < spec->pcm_vol_nodes; i++) {		err = create_mixer(codec, spec->pcm_vol[i].node,				   spec->pcm_vol[i].index,				   names[i], "Playback", 0);		if (err < 0)			return err;	}	return 0;}static int build_output_controls(struct hda_codec *codec){	struct hda_gspec *spec = codec->spec;	static const char *types_speaker[] = { "Speaker", "Headphone" };	static const char *types_line[] = { "Front", "Headphone" };	switch (spec->pcm_vol_nodes) {	case 1:		return create_mixer(codec, spec->pcm_vol[0].node,				    spec->pcm_vol[0].index,				    "Master", "Playback", 0);	case 2:		if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)			return create_output_mixers(codec, types_speaker);		else			return create_output_mixers(codec, types_line);	}	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 i, err;	static struct snd_kcontrol_new cap_sel = {		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		.name = "Capture Source",		.info = capture_source_info,		.get = capture_source_get,		.put = capture_source_put,	};	if (! adc_node || ! spec->input_mux.num_items)		return 0; /* not found */	spec->cur_cap_src = 0;	select_input_connection(codec, adc_node,				spec->input_mux.items[0].index);	/* create capture volume and switch controls if the ADC has an amp */	/* do we have only a single item? */	if (spec->input_mux.num_items == 1) {		err = create_mixer(codec, adc_node,				   spec->input_mux.items[0].index,				   NULL, "Capture", 0);		if (err < 0)			return err;		return 0;	}	/* create input MUX if multiple sources are available */	if ((err = snd_ctl_add(codec->bus->card,			       snd_ctl_new1(&cap_sel, codec))) < 0)		return err;	/* no volume control? */	if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||	    ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))		return 0;	for (i = 0; i < spec->input_mux.num_items; i++) {		struct snd_kcontrol_new knew;		char name[32];		sprintf(name, "%s Capture Volume",			spec->input_mux.items[i].label);		knew = (struct snd_kcontrol_new)			HDA_CODEC_VOLUME(name, adc_node->nid,					 spec->input_mux.items[i].index,					 HDA_INPUT);		if ((err = snd_ctl_add(codec->bus->card,				       snd_ctl_new1(&knew, codec))) < 0)			return err;	}	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", 1);				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 hda_gnode *node;	int err;	const char *type;	if (! spec->out_pin_node[0])		return 0;	list_for_each_entry(node, &spec->nid_list, 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[0],						  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 generic_pcm2_prepare(struct hda_pcm_stream *hinfo,				struct hda_codec *codec,				unsigned int stream_tag,				unsigned int format,				struct snd_pcm_substream *substream){	struct hda_gspec *spec = codec->spec;	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);	snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,				   stream_tag, 0, format);	return 0;}static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,				struct hda_codec *codec,				struct snd_pcm_substream *substream){	struct hda_gspec *spec = codec->spec;	snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);	snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0);	return 0;}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[0] && ! 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[0]) {		info->stream[0] = generic_pcm_playback;		info->stream[0].nid = spec->dac_node[0]->nid;		if (spec->dac_node[1]) {			info->stream[0].ops.prepare = generic_pcm2_prepare;			info->stream[0].ops.cleanup = generic_pcm2_cleanup;		}	}	if (spec->adc_node) {		info->stream[1] = generic_pcm_playback;		info->stream[1].nid = spec->adc_node->nid;	}	return 0;}#ifdef CONFIG_SND_HDA_POWER_SAVEstatic int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid){	struct hda_gspec *spec = codec->spec;	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);}#endif/* */static struct hda_codec_ops generic_patch_ops = {	.build_controls = build_generic_controls,	.build_pcms = build_generic_pcms,	.free = snd_hda_generic_free,#ifdef CONFIG_SND_HDA_POWER_SAVE	.check_power_status = generic_check_power_status,#endif};/* * 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 + -