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

📄 hda_codec.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 __devinit set_pcm_default_values(struct hda_codec *codec,					    struct hda_pcm_stream *info){	/* query support PCM information from the given NID */	if (info->nid && (!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 __devinit snd_hda_build_pcms(struct hda_bus *bus){	struct hda_codec *codec;	list_for_each_entry(codec, &bus->codec_list, 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 * @num_configs: number of config enums * @models: array of model name strings * @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,			       int num_configs, const char **models,			       const struct snd_pci_quirk *tbl){	if (codec->bus->modelname && models) {		int i;		for (i = 0; i < num_configs; i++) {			if (models[i] &&			    !strcmp(codec->bus->modelname, models[i])) {				snd_printd(KERN_INFO "hda_codec: model '%s' is "					   "selected\n", models[i]);				return i;			}		}	}	if (!codec->bus->pci || !tbl)		return -1;	tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);	if (!tbl)		return -1;	if (tbl->value >= 0 && tbl->value < num_configs) {#ifdef CONFIG_SND_DEBUG_DETECT		char tmp[10];		const char *model = NULL;		if (models)			model = models[tbl->value];		if (!model) {			sprintf(tmp, "#%d", tbl->value);			model = tmp;		}		snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "			    "for config %x:%x (%s)\n",			    model, tbl->subvendor, tbl->subdevice,			    (tbl->name ? tbl->name : "Unknown device"));#endif		return tbl->value;	}	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;			err = snd_ctl_add(codec->bus->card, kctl);			if (err < 0)				return err;		}	}	return 0;}#ifdef CONFIG_SND_HDA_POWER_SAVEstatic void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,				unsigned int power_state);static void hda_power_work(struct work_struct *work){	struct hda_codec *codec =		container_of(work, struct hda_codec, power_work.work);	if (!codec->power_on || codec->power_count) {		codec->power_transition = 0;		return;	}	hda_call_codec_suspend(codec);	if (codec->bus->ops.pm_notify)		codec->bus->ops.pm_notify(codec);}static void hda_keep_power_on(struct hda_codec *codec){	codec->power_count++;	codec->power_on = 1;}void snd_hda_power_up(struct hda_codec *codec){	codec->power_count++;	if (codec->power_on || codec->power_transition)		return;	codec->power_on = 1;	if (codec->bus->ops.pm_notify)		codec->bus->ops.pm_notify(codec);	hda_call_codec_resume(codec);	cancel_delayed_work(&codec->power_work);	codec->power_transition = 0;}void snd_hda_power_down(struct hda_codec *codec){	--codec->power_count;	if (!codec->power_on || codec->power_count || codec->power_transition)		return;	if (power_save) {		codec->power_transition = 1; /* avoid reentrance */		schedule_delayed_work(&codec->power_work,				      msecs_to_jiffies(power_save * 1000));	}}int snd_hda_check_amp_list_power(struct hda_codec *codec,				 struct hda_loopback_check *check,				 hda_nid_t nid){	struct hda_amp_list *p;	int ch, v;	if (!check->amplist)		return 0;	for (p = check->amplist; p->nid; p++) {		if (p->nid == nid)			break;	}	if (!p->nid)		return 0; /* nothing changed */	for (p = check->amplist; p->nid; p++) {		for (ch = 0; ch < 2; ch++) {			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,						   p->idx);			if (!(v & HDA_AMP_MUTE) && v > 0) {				if (!check->power_on) {					check->power_on = 1;					snd_hda_power_up(codec);				}				return 1;			}		}	}	if (check->power_on) {		check->power_on = 0;		snd_hda_power_down(codec);	}	return 0;}#endif/* * 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)		return 0;	/* change the current channel setting */	*max_channelsp = chmode[mode].channels;	if (chmode[mode].sequence)		snd_hda_sequence_write_cache(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;	if (!imux->num_items)		return 0;	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;	if (!imux->num_items)		return 0;	idx = ucontrol->value.enumerated.item[0];	if (idx >= imux->num_items)		idx = imux->num_items - 1;	if (*cur_val == idx)		return 0;	snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,				  imux->items[idx].index);	*cur_val = idx;	return 1;}/* * Multi-channel / digital-out PCM helper functions *//* setup SPDIF output stream */static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,				 unsigned int stream_tag, unsigned int format){	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */	if (codec->spdif_ctls & AC_DIG1_ENABLE)		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);	/* turn on again (if needed) */	if (codec->spdif_ctls & AC_DIG1_ENABLE)		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,				    codec->spdif_ctls & 0xff);}/* * open the digital out in the exclusive mode */int snd_hda_multi_out_dig_open(struct hda_codec *codec,			       struct hda_multi_out *mout){	mutex_lock(&codec->spdif_mutex);	if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)		/* already opened as analog dup; reset it once */		snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);	mout->dig_out_used = HDA_DIG_EXCLUSIVE;	mutex_unlock(&codec->spdif_mutex);	return 0;}int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,				  struct hda_multi_out *mout,				  unsigned int stream_tag,				  unsigned int format,				  struct snd_pcm_substream *substream){	mutex_lock(&codec->spdif_mutex);	setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);	mutex_unlock(&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){	mutex_lock(&codec->spdif_mutex);	mout->dig_out_used = 0;	mutex_unlock(&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,				  struct snd_pcm_substream *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,				     struct snd_pcm_substream *substream){	hda_nid_t *nids = mout->dac_nids;	int chs = substream->runtime->channels;	int i;	mutex_lock(&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

⌨️ 快捷键说明

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