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

📄 hda_codec.c

📁 LINUX 2.6.17.4的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
		else if (maxbps >= 24)			val |= 0x30;		else			val |= 0x20;		break;	default:		snd_printdd("invalid format width %d\n", snd_pcm_format_width(format));		return 0;	}	return val;}/** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec * @nid: NID to query * @ratesp: the pointer to store the detected rate bitflags * @formatsp: the pointer to store the detected formats * @bpsp: the pointer to store the detected format widths * * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp * or @bsps argument is ignored. * * Returns 0 if successful, otherwise a negative error code. */int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,				u32 *ratesp, u64 *formatsp, unsigned int *bpsp){	int i;	unsigned int val, streams;	val = 0;	if (nid != codec->afg &&	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);		if (val == -1)			return -EIO;	}	if (! val)		val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);	if (ratesp) {		u32 rates = 0;		for (i = 0; rate_bits[i].hz; i++) {			if (val & (1 << i))				rates |= rate_bits[i].alsa_bits;		}		*ratesp = rates;	}	if (formatsp || bpsp) {		u64 formats = 0;		unsigned int bps;		unsigned int wcaps;		wcaps = get_wcaps(codec, nid);		streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);		if (streams == -1)			return -EIO;		if (! streams) {			streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);			if (streams == -1)				return -EIO;		}		bps = 0;		if (streams & AC_SUPFMT_PCM) {			if (val & AC_SUPPCM_BITS_8) {				formats |= SNDRV_PCM_FMTBIT_U8;				bps = 8;			}			if (val & AC_SUPPCM_BITS_16) {				formats |= SNDRV_PCM_FMTBIT_S16_LE;				bps = 16;			}			if (wcaps & AC_WCAP_DIGITAL) {				if (val & AC_SUPPCM_BITS_32)					formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;				if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))					formats |= SNDRV_PCM_FMTBIT_S32_LE;				if (val & AC_SUPPCM_BITS_24)					bps = 24;				else if (val & AC_SUPPCM_BITS_20)					bps = 20;			} else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) {				formats |= SNDRV_PCM_FMTBIT_S32_LE;				if (val & AC_SUPPCM_BITS_32)					bps = 32;				else if (val & AC_SUPPCM_BITS_20)					bps = 20;				else if (val & AC_SUPPCM_BITS_24)					bps = 24;			}		}		else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;			bps = 32;		} else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */			/* temporary hack: we have still no proper support			 * for the direct AC3 stream...			 */			formats |= SNDRV_PCM_FMTBIT_U8;			bps = 8;		}		if (formatsp)			*formatsp = formats;		if (bpsp)			*bpsp = bps;	}	return 0;}/** * snd_hda_is_supported_format - check whether the given node supports the format val * * Returns 1 if supported, 0 if not. */int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,				unsigned int format){	int i;	unsigned int val = 0, rate, stream;	if (nid != codec->afg &&	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);		if (val == -1)			return 0;	}	if (! val) {		val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);		if (val == -1)			return 0;	}	rate = format & 0xff00;	for (i = 0; rate_bits[i].hz; i++)		if (rate_bits[i].hda_fmt == rate) {			if (val & (1 << i))				break;			return 0;		}	if (! rate_bits[i].hz)		return 0;	stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);	if (stream == -1)		return 0;	if (! stream && nid != codec->afg)		stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);	if (! stream || stream == -1)		return 0;	if (stream & AC_SUPFMT_PCM) {		switch (format & 0xf0) {		case 0x00:			if (! (val & AC_SUPPCM_BITS_8))				return 0;			break;		case 0x10:			if (! (val & AC_SUPPCM_BITS_16))				return 0;			break;		case 0x20:			if (! (val & AC_SUPPCM_BITS_20))				return 0;			break;		case 0x30:			if (! (val & AC_SUPPCM_BITS_24))				return 0;			break;		case 0x40:			if (! (val & AC_SUPPCM_BITS_32))				return 0;			break;		default:			return 0;		}	} else {		/* FIXME: check for float32 and AC3? */	}	return 1;}/* * PCM stuff */static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,				      struct hda_codec *codec,				      struct snd_pcm_substream *substream){	return 0;}static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,				   struct hda_codec *codec,				   unsigned int stream_tag,				   unsigned int format,				   struct snd_pcm_substream *substream){	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);	return 0;}static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,				   struct hda_codec *codec,				   struct snd_pcm_substream *substream){	snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);	return 0;}static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info){	if (info->nid) {		/* query support PCM information from the given NID */		if (! info->rates || ! info->formats)			snd_hda_query_supported_pcm(codec, info->nid,						    info->rates ? NULL : &info->rates,						    info->formats ? NULL : &info->formats,						    info->maxbps ? NULL : &info->maxbps);	}	if (info->ops.open == NULL)		info->ops.open = hda_pcm_default_open_close;	if (info->ops.close == NULL)		info->ops.close = hda_pcm_default_open_close;	if (info->ops.prepare == NULL) {		snd_assert(info->nid, return -EINVAL);		info->ops.prepare = hda_pcm_default_prepare;	}	if (info->ops.cleanup == NULL) {		snd_assert(info->nid, return -EINVAL);		info->ops.cleanup = hda_pcm_default_cleanup;	}	return 0;}/** * snd_hda_build_pcms - build PCM information * @bus: the BUS * * Create PCM information for each codec included in the bus. * * The build_pcms codec patch is requested to set up codec->num_pcms and * codec->pcm_info properly.  The array is referred by the top-level driver * to create its PCM instances. * The allocated codec->pcm_info should be released in codec->patch_ops.free * callback. * * At least, substreams, channels_min and channels_max must be filled for * each stream.  substreams = 0 indicates that the stream doesn't exist. * When rates and/or formats are zero, the supported values are queried * from the given nid.  The nid is used also by the default ops.prepare * and ops.cleanup callbacks. * * 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 struct snd_kcontrol_new * * 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, struct snd_kcontrol_new *knew){	int err;	for (; knew->name; knew++) {		struct snd_kcontrol *kctl;		kctl = snd_ctl_new1(knew, codec);		if (! kctl)			return -ENOMEM;		err = snd_ctl_add(codec->bus->card, kctl);		if (err < 0) {			if (! codec->addr)				return err;			kctl = snd_ctl_new1(knew, codec);			if (! kctl)				return -ENOMEM;			kctl->id.device = codec->addr;			if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)				return err;		}	}	return 0;}/* * Channel mode helper */int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo,			 const struct hda_channel_mode *chmode, int num_chmodes){	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;	uinfo->count = 1;	uinfo->value.enumerated.items = num_chmodes;	if (uinfo->value.enumerated.item >= num_chmodes)		uinfo->value.enumerated.item = num_chmodes - 1;	sprintf(uinfo->value.enumerated.name, "%dch",		chmode[uinfo->value.enumerated.item].channels);	return 0;}int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,			const struct hda_channel_mode *chmode, int num_chmodes,			int max_channels){	int i;	for (i = 0; i < num_chmodes; i++) {		if (max_channels == chmode[i].channels) {			ucontrol->value.enumerated.item[0] = i;			break;		}	}	return 0;}int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,			const struct hda_channel_mode *chmode, int num_chmodes,			int *max_channelsp){	unsigned int mode;	mode = ucontrol->value.enumerated.item[0];	snd_assert(mode < num_chmodes, return -EINVAL);	if (*max_channelsp == chmode[mode].channels && ! codec->in_resume)		return 0;	/* change the current channel setting */	*max_channelsp = chmode[mode].channels;	if (chmode[mode].sequence)		snd_hda_sequence_write(codec, chmode[mode].sequence);	return 1;}/* * input MUX helper */int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *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,			  struct snd_ctl_elem_value *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

⌨️ 快捷键说明

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