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

📄 hda_codec.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 4 页
字号:
 * The driver needs to call ops.open in its open callback.  Similarly, * ops.close is supposed to be called in the close callback. * ops.prepare should be called in the prepare or hw_params callback * with the proper parameters for set up. * ops.cleanup should be called in hw_free for clean up of streams. * * This function returns 0 if successfull, or a negative error code. */int snd_hda_build_pcms(struct hda_bus *bus){	struct list_head *p;	list_for_each(p, &bus->codec_list) {		struct hda_codec *codec = list_entry(p, struct hda_codec, list);		unsigned int pcm, s;		int err;		if (! codec->patch_ops.build_pcms)			continue;		err = codec->patch_ops.build_pcms(codec);		if (err < 0)			return err;		for (pcm = 0; pcm < codec->num_pcms; pcm++) {			for (s = 0; s < 2; s++) {				struct hda_pcm_stream *info;				info = &codec->pcm_info[pcm].stream[s];				if (! info->substreams)					continue;				err = set_pcm_default_values(codec, info);				if (err < 0)					return err;			}		}	}	return 0;}/** * snd_hda_check_board_config - compare the current codec with the config table * @codec: the HDA codec * @tbl: configuration table, terminated by null entries * * Compares the modelname or PCI subsystem id of the current codec with the * given configuration table.  If a matching entry is found, returns its * config value (supposed to be 0 or positive). * * If no entries are matching, the function returns a negative value. */int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl){	const struct hda_board_config *c;	if (codec->bus->modelname) {		for (c = tbl; c->modelname || c->pci_subvendor; c++) {			if (c->modelname &&			    ! strcmp(codec->bus->modelname, c->modelname)) {				snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);				return c->config;			}		}	}	if (codec->bus->pci) {		u16 subsystem_vendor, subsystem_device;		pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);		pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);		for (c = tbl; c->modelname || c->pci_subvendor; c++) {			if (c->pci_subvendor == subsystem_vendor &&			    (! c->pci_subdevice /* all match */||			     (c->pci_subdevice == subsystem_device))) {				snd_printdd(KERN_INFO "hda_codec: PCI %x:%x, codec config %d is selected\n",					    subsystem_vendor, subsystem_device, c->config);				return c->config;			}		}	}	return -1;}/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of snd_kcontrol_new_t * * This helper function creates and add new controls in the given array. * The array must be terminated with an empty entry as terminator. * * Returns 0 if successful, or a negative error code. */int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew){	int err;	for (; knew->name; knew++) {		err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec));		if (err < 0)			return err;	}	return 0;}/* * input MUX helper */int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo){	unsigned int index;	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;	uinfo->count = 1;	uinfo->value.enumerated.items = imux->num_items;	index = uinfo->value.enumerated.item;	if (index >= imux->num_items)		index = imux->num_items - 1;	strcpy(uinfo->value.enumerated.name, imux->items[index].label);	return 0;}int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,			  snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,			  unsigned int *cur_val){	unsigned int idx;	idx = ucontrol->value.enumerated.item[0];	if (idx >= imux->num_items)		idx = imux->num_items - 1;	if (*cur_val == idx && ! codec->in_resume)		return 0;	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,			    imux->items[idx].index);	*cur_val = idx;	return 1;}/* * Multi-channel / digital-out PCM helper functions *//* * open the digital out in the exclusive mode */int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout){	down(&codec->spdif_mutex);	if (mout->dig_out_used) {		up(&codec->spdif_mutex);		return -EBUSY; /* already being used */	}	mout->dig_out_used = HDA_DIG_EXCLUSIVE;	up(&codec->spdif_mutex);	return 0;}/* * release the digital out */int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout){	down(&codec->spdif_mutex);	mout->dig_out_used = 0;	up(&codec->spdif_mutex);	return 0;}/* * set up more restrictions for analog out */int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,				  snd_pcm_substream_t *substream){	substream->runtime->hw.channels_max = mout->max_channels;	return snd_pcm_hw_constraint_step(substream->runtime, 0,					  SNDRV_PCM_HW_PARAM_CHANNELS, 2);}/* * set up the i/o for analog out * when the digital out is available, copy the front out to digital out, too. */int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,				     unsigned int stream_tag,				     unsigned int format,				     snd_pcm_substream_t *substream){	hda_nid_t *nids = mout->dac_nids;	int chs = substream->runtime->channels;	int i;	down(&codec->spdif_mutex);	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {		if (chs == 2 &&		    snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&		    ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {			mout->dig_out_used = HDA_DIG_ANALOG_DUP;			/* setup digital receiver */			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,						   stream_tag, 0, format);		} else {			mout->dig_out_used = 0;			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);		}	}	up(&codec->spdif_mutex);	/* front */	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);	if (mout->hp_nid)		/* headphone out will just decode front left/right (stereo) */		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);	/* surrounds */	for (i = 1; i < mout->num_dacs; i++) {		if (chs >= (i + 1) * 2) /* independent out */			snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,						   format);		else /* copy front */			snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0,						   format);	}	return 0;}/* * clean up the setting for analog out */int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout){	hda_nid_t *nids = mout->dac_nids;	int i;	for (i = 0; i < mout->num_dacs; i++)		snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);	if (mout->hp_nid)		snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);	down(&codec->spdif_mutex);	if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {		snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);		mout->dig_out_used = 0;	}	up(&codec->spdif_mutex);	return 0;}/* * Helper for automatic ping configuration *//* parse all pin widgets and store the useful pin nids to cfg */int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg){	hda_nid_t nid, nid_start;	int i, j, nodes;	short seq, sequences[4], assoc_line_out;	memset(cfg, 0, sizeof(*cfg));	memset(sequences, 0, sizeof(sequences));	assoc_line_out = 0;	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);	for (nid = nid_start; nid < nodes + nid_start; nid++) {		unsigned int wid_caps = snd_hda_param_read(codec, nid,							   AC_PAR_AUDIO_WIDGET_CAP);		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;		unsigned int def_conf;		short assoc, loc;		/* read all default configuration for pin complex */		if (wid_type != AC_WID_PIN)			continue;		def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)			continue;		loc = get_defcfg_location(def_conf);		switch (get_defcfg_device(def_conf)) {		case AC_JACK_LINE_OUT:		case AC_JACK_SPEAKER:			seq = get_defcfg_sequence(def_conf);			assoc = get_defcfg_association(def_conf);			if (! assoc)				continue;			if (! assoc_line_out)				assoc_line_out = assoc;			else if (assoc_line_out != assoc)				continue;			if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))				continue;			cfg->line_out_pins[cfg->line_outs] = nid;			sequences[cfg->line_outs] = seq;			cfg->line_outs++;			break;		case AC_JACK_HP_OUT:			cfg->hp_pin = nid;			break;		case AC_JACK_MIC_IN:			if (loc == AC_JACK_LOC_FRONT)				cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid;			else				cfg->input_pins[AUTO_PIN_MIC] = nid;			break;		case AC_JACK_LINE_IN:			if (loc == AC_JACK_LOC_FRONT)				cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;			else				cfg->input_pins[AUTO_PIN_LINE] = nid;			break;		case AC_JACK_CD:			cfg->input_pins[AUTO_PIN_CD] = nid;			break;		case AC_JACK_AUX:			cfg->input_pins[AUTO_PIN_AUX] = nid;			break;		case AC_JACK_SPDIF_OUT:			cfg->dig_out_pin = nid;			break;		case AC_JACK_SPDIF_IN:			cfg->dig_in_pin = nid;			break;		}	}	/* sort by sequence */	for (i = 0; i < cfg->line_outs; i++)		for (j = i + 1; j < cfg->line_outs; j++)			if (sequences[i] > sequences[j]) {				seq = sequences[i];				sequences[i] = sequences[j];				sequences[j] = seq;				nid = cfg->line_out_pins[i];				cfg->line_out_pins[i] = cfg->line_out_pins[j];				cfg->line_out_pins[j] = nid;			}	/* Reorder the surround channels	 * ALSA sequence is front/surr/clfe/side	 * HDA sequence is:	 *    4-ch: front/surr  =>  OK as it is	 *    6-ch: front/clfe/surr	 *    8-ch: front/clfe/side/surr	 */	switch (cfg->line_outs) {	case 3:		nid = cfg->line_out_pins[1];		cfg->line_out_pins[1] = cfg->line_out_pins[2];		cfg->line_out_pins[2] = nid;		break;	case 4:		nid = cfg->line_out_pins[1];		cfg->line_out_pins[1] = cfg->line_out_pins[3];		cfg->line_out_pins[3] = cfg->line_out_pins[2];		cfg->line_out_pins[2] = nid;		break;	}	return 0;}#ifdef CONFIG_PM/* * power management *//** * snd_hda_suspend - suspend the codecs * @bus: the HDA bus * @state: suspsend state * * Returns 0 if successful. */int snd_hda_suspend(struct hda_bus *bus, pm_message_t state){	struct list_head *p;	/* FIXME: should handle power widget capabilities */	list_for_each(p, &bus->codec_list) {		struct hda_codec *codec = list_entry(p, struct hda_codec, list);		if (codec->patch_ops.suspend)			codec->patch_ops.suspend(codec, state);	}	return 0;}/** * snd_hda_resume - resume the codecs * @bus: the HDA bus * @state: resume state * * Returns 0 if successful. */int snd_hda_resume(struct hda_bus *bus){	struct list_head *p;	list_for_each(p, &bus->codec_list) {		struct hda_codec *codec = list_entry(p, struct hda_codec, list);		if (codec->patch_ops.resume)			codec->patch_ops.resume(codec);	}	return 0;}/** * snd_hda_resume_ctls - resume controls in the new control list * @codec: the HDA codec * @knew: the array of snd_kcontrol_new_t * * This function resumes the mixer controls in the snd_kcontrol_new_t array, * originally for snd_hda_add_new_ctls(). * The array must be terminated with an empty entry as terminator. */int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew){	snd_ctl_elem_value_t *val;	val = kmalloc(sizeof(*val), GFP_KERNEL);	if (! val)		return -ENOMEM;	codec->in_resume = 1;	for (; knew->name; knew++) {		int i, count;		count = knew->count ? knew->count : 1;		for (i = 0; i < count; i++) {			memset(val, 0, sizeof(*val));			val->id.iface = knew->iface;			val->id.device = knew->device;			val->id.subdevice = knew->subdevice;			strcpy(val->id.name, knew->name);			val->id.index = knew->index ? knew->index : i;			/* Assume that get callback reads only from cache,			 * not accessing to the real hardware			 */			if (snd_ctl_elem_read(codec->bus->card, val) < 0)				continue;			snd_ctl_elem_write(codec->bus->card, NULL, val);		}	}	codec->in_resume = 0;	kfree(val);	return 0;}/** * snd_hda_resume_spdif_out - resume the digital out * @codec: the HDA codec */int snd_hda_resume_spdif_out(struct hda_codec *codec){	return snd_hda_resume_ctls(codec, dig_mixes);}/** * snd_hda_resume_spdif_in - resume the digital in * @codec: the HDA codec */int snd_hda_resume_spdif_in(struct hda_codec *codec){	return snd_hda_resume_ctls(codec, dig_in_ctls);}#endif/* * symbols exported for controller modules */EXPORT_SYMBOL(snd_hda_codec_read);EXPORT_SYMBOL(snd_hda_codec_write);EXPORT_SYMBOL(snd_hda_sequence_write);EXPORT_SYMBOL(snd_hda_get_sub_nodes);EXPORT_SYMBOL(snd_hda_queue_unsol_event);EXPORT_SYMBOL(snd_hda_bus_new);EXPORT_SYMBOL(snd_hda_codec_new);EXPORT_SYMBOL(snd_hda_codec_setup_stream);EXPORT_SYMBOL(snd_hda_calc_stream_format);EXPORT_SYMBOL(snd_hda_build_pcms);EXPORT_SYMBOL(snd_hda_build_controls);#ifdef CONFIG_PMEXPORT_SYMBOL(snd_hda_suspend);EXPORT_SYMBOL(snd_hda_resume);#endif/* *  INIT part */static int __init alsa_hda_init(void){	return 0;}static void __exit alsa_hda_exit(void){}module_init(alsa_hda_init)module_exit(alsa_hda_exit)

⌨️ 快捷键说明

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