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

📄 hda_codec.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	ucontrol->value.iec958.status[3] = sbits >> 24;	return 0;}static struct snd_kcontrol_new dig_in_ctls[] = {	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),		.info = snd_hda_spdif_in_switch_info,		.get = snd_hda_spdif_in_switch_get,		.put = snd_hda_spdif_in_switch_put,	},	{		.access = SNDRV_CTL_ELEM_ACCESS_READ,		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),		.info = snd_hda_spdif_mask_info,		.get = snd_hda_spdif_in_status_get,	},	{ } /* end */};/** * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls * @codec: the HDA codec * @nid: audio in widget NID * * Creates controls related with the SPDIF input. * Called from each patch supporting the SPDIF in. * * Returns 0 if successful, or a negative error code. */int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid){	int err;	struct snd_kcontrol *kctl;	struct snd_kcontrol_new *dig_mix;	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {		kctl = snd_ctl_new1(dig_mix, codec);		kctl->private_value = nid;		err = snd_ctl_add(codec->bus->card, kctl);		if (err < 0)			return err;	}	codec->spdif_in_enable =		snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &		AC_DIG1_ENABLE;	return 0;}#ifdef SND_HDA_NEEDS_RESUME/* * command cache *//* build a 32bit cache key with the widget id and the command parameter */#define build_cmd_cache_key(nid, verb)	((verb << 8) | nid)#define get_cmd_cache_nid(key)		((key) & 0xff)#define get_cmd_cache_cmd(key)		(((key) >> 8) & 0xffff)/** * snd_hda_codec_write_cache - send a single command with caching * @codec: the HDA codec * @nid: NID to send the command * @direct: direct flag * @verb: the verb to send * @parm: the parameter for the verb * * Send a single command without waiting for response. * * Returns 0 if successful, or a negative error code. */int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,			      int direct, unsigned int verb, unsigned int parm){	int err;	snd_hda_power_up(codec);	mutex_lock(&codec->bus->cmd_mutex);	err = codec->bus->ops.command(codec, nid, direct, verb, parm);	if (!err) {		struct hda_cache_head *c;		u32 key = build_cmd_cache_key(nid, verb);		c = get_alloc_hash(&codec->cmd_cache, key);		if (c)			c->val = parm;	}	mutex_unlock(&codec->bus->cmd_mutex);	snd_hda_power_down(codec);	return err;}/* resume the all commands from the cache */void snd_hda_codec_resume_cache(struct hda_codec *codec){	struct hda_cache_head *buffer = codec->cmd_cache.buffer;	int i;	for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {		u32 key = buffer->key;		if (!key)			continue;		snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,				    get_cmd_cache_cmd(key), buffer->val);	}}/** * snd_hda_sequence_write_cache - sequence writes with caching * @codec: the HDA codec * @seq: VERB array to send * * Send the commands sequentially from the given array. * Thte commands are recorded on cache for power-save and resume. * The array must be terminated with NID=0. */void snd_hda_sequence_write_cache(struct hda_codec *codec,				  const struct hda_verb *seq){	for (; seq->nid; seq++)		snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,					  seq->param);}#endif /* SND_HDA_NEEDS_RESUME *//* * set power state of the codec */static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,				unsigned int power_state){	hda_nid_t nid;	int i;	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,			    power_state);	nid = codec->start_nid;	for (i = 0; i < codec->num_nodes; i++, nid++) {		unsigned int wcaps = get_wcaps(codec, nid);		if (wcaps & AC_WCAP_POWER) {			unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>				AC_WCAP_TYPE_SHIFT;			if (wid_type == AC_WID_PIN) {				unsigned int pincap;				/*				 * don't power down the widget if it controls				 * eapd and EAPD_BTLENABLE is set.				 */				pincap = snd_hda_param_read(codec, nid,							    AC_PAR_PIN_CAP);				if (pincap & AC_PINCAP_EAPD) {					int eapd = snd_hda_codec_read(codec,						nid, 0,						AC_VERB_GET_EAPD_BTLENABLE, 0);					eapd &= 0x02;					if (power_state == AC_PWRST_D3 && eapd)						continue;				}			}			snd_hda_codec_write(codec, nid, 0,					    AC_VERB_SET_POWER_STATE,					    power_state);		}	}	if (power_state == AC_PWRST_D0) {		unsigned long end_time;		int state;		msleep(10);		/* wait until the codec reachs to D0 */		end_time = jiffies + msecs_to_jiffies(500);		do {			state = snd_hda_codec_read(codec, fg, 0,						   AC_VERB_GET_POWER_STATE, 0);			if (state == power_state)				break;			msleep(1);		} while (time_after_eq(end_time, jiffies));	}}#ifdef SND_HDA_NEEDS_RESUME/* * call suspend and power-down; used both from PM and power-save */static void hda_call_codec_suspend(struct hda_codec *codec){	if (codec->patch_ops.suspend)		codec->patch_ops.suspend(codec, PMSG_SUSPEND);	hda_set_power_state(codec,			    codec->afg ? codec->afg : codec->mfg,			    AC_PWRST_D3);#ifdef CONFIG_SND_HDA_POWER_SAVE	cancel_delayed_work(&codec->power_work);	codec->power_on = 0;	codec->power_transition = 0;#endif}/* * kick up codec; used both from PM and power-save */static void hda_call_codec_resume(struct hda_codec *codec){	hda_set_power_state(codec,			    codec->afg ? codec->afg : codec->mfg,			    AC_PWRST_D0);	if (codec->patch_ops.resume)		codec->patch_ops.resume(codec);	else {		if (codec->patch_ops.init)			codec->patch_ops.init(codec);		snd_hda_codec_resume_amp(codec);		snd_hda_codec_resume_cache(codec);	}}#endif /* SND_HDA_NEEDS_RESUME *//** * snd_hda_build_controls - build mixer controls * @bus: the BUS * * Creates mixer controls for each codec included in the bus. * * Returns 0 if successful, otherwise a negative error code. */int __devinit snd_hda_build_controls(struct hda_bus *bus){	struct hda_codec *codec;	list_for_each_entry(codec, &bus->codec_list, list) {		int err = 0;		/* fake as if already powered-on */		hda_keep_power_on(codec);		/* then fire up */		hda_set_power_state(codec,				    codec->afg ? codec->afg : codec->mfg,				    AC_PWRST_D0);		/* continue to initialize... */		if (codec->patch_ops.init)			err = codec->patch_ops.init(codec);		if (!err && codec->patch_ops.build_controls)			err = codec->patch_ops.build_controls(codec);		snd_hda_power_down(codec);		if (err < 0)			return err;	}	return 0;}/* * stream formats */struct hda_rate_tbl {	unsigned int hz;	unsigned int alsa_bits;	unsigned int hda_fmt;};static struct hda_rate_tbl rate_bits[] = {	/* rate in Hz, ALSA rate bitmask, HDA format value */	/* autodetected value used in snd_hda_query_supported_pcm */	{ 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */	{ 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */	{ 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */	{ 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */	{ 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */	{ 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */	{ 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */	{ 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */	{ 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */	{ 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */	{ 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */#define AC_PAR_PCM_RATE_BITS	11	/* up to bits 10, 384kHZ isn't supported properly */	/* not autodetected value */	{ 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */	{ 0 } /* terminator */};/** * snd_hda_calc_stream_format - calculate format bitset * @rate: the sample rate * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps * * Calculate the format bitset from the given rate, channels and th PCM format. * * Return zero if invalid. */unsigned int snd_hda_calc_stream_format(unsigned int rate,					unsigned int channels,					unsigned int format,					unsigned int maxbps){	int i;	unsigned int val = 0;	for (i = 0; rate_bits[i].hz; i++)		if (rate_bits[i].hz == rate) {			val = rate_bits[i].hda_fmt;			break;		}	if (!rate_bits[i].hz) {		snd_printdd("invalid rate %d\n", rate);		return 0;	}	if (channels == 0 || channels > 8) {		snd_printdd("invalid channels %d\n", channels);		return 0;	}	val |= channels - 1;	switch (snd_pcm_format_width(format)) {	case 8:  val |= 0x00; break;	case 16: val |= 0x10; break;	case 20:	case 24:	case 32:		if (maxbps >= 32)			val |= 0x40;		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; i < AC_PAR_PCM_RATE_BITS; 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_24)					bps = 24;				else if (val & AC_SUPPCM_BITS_20)					bps = 20;			}		}		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; i < AC_PAR_PCM_RATE_BITS; i++)		if (rate_bits[i].hda_fmt == rate) {			if (val & (1 << i))				break;			return 0;		}	if (i >= AC_PAR_PCM_RATE_BITS)		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;

⌨️ 快捷键说明

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