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

📄 hda_codec.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * read widget caps for each widget and store in cache */static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node){	int i;	hda_nid_t nid;	codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node,						 &codec->start_nid);	codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL);	if (!codec->wcaps)		return -ENOMEM;	nid = codec->start_nid;	for (i = 0; i < codec->num_nodes; i++, nid++)		codec->wcaps[i] = snd_hda_param_read(codec, nid,						     AC_PAR_AUDIO_WIDGET_CAP);	return 0;}static void init_hda_cache(struct hda_cache_rec *cache,			   unsigned int record_size);static void free_hda_cache(struct hda_cache_rec *cache);/* * codec destructor */static void snd_hda_codec_free(struct hda_codec *codec){	if (!codec)		return;#ifdef CONFIG_SND_HDA_POWER_SAVE	cancel_delayed_work(&codec->power_work);	flush_scheduled_work();#endif	list_del(&codec->list);	codec->bus->caddr_tbl[codec->addr] = NULL;	if (codec->patch_ops.free)		codec->patch_ops.free(codec);	free_hda_cache(&codec->amp_cache);	free_hda_cache(&codec->cmd_cache);	kfree(codec->wcaps);	kfree(codec);}/** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign * @codec_addr: the codec address * @codecp: the pointer to store the generated codec * * Returns 0 if successful, or a negative error code. */int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,				struct hda_codec **codecp){	struct hda_codec *codec;	char component[13];	int err;	snd_assert(bus, return -EINVAL);	snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);	if (bus->caddr_tbl[codec_addr]) {		snd_printk(KERN_ERR "hda_codec: "			   "address 0x%x is already occupied\n", codec_addr);		return -EBUSY;	}	codec = kzalloc(sizeof(*codec), GFP_KERNEL);	if (codec == NULL) {		snd_printk(KERN_ERR "can't allocate struct hda_codec\n");		return -ENOMEM;	}	codec->bus = bus;	codec->addr = codec_addr;	mutex_init(&codec->spdif_mutex);	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));#ifdef CONFIG_SND_HDA_POWER_SAVE	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);	/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.	 * the caller has to power down appropriatley after initialization	 * phase.	 */	hda_keep_power_on(codec);#endif	list_add_tail(&codec->list, &bus->codec_list);	bus->caddr_tbl[codec_addr] = codec;	codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,					      AC_PAR_VENDOR_ID);	if (codec->vendor_id == -1)		/* read again, hopefully the access method was corrected		 * in the last read...		 */		codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,						      AC_PAR_VENDOR_ID);	codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT,						 AC_PAR_SUBSYSTEM_ID);	codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT,						AC_PAR_REV_ID);	setup_fg_nodes(codec);	if (!codec->afg && !codec->mfg) {		snd_printdd("hda_codec: no AFG or MFG node found\n");		snd_hda_codec_free(codec);		return -ENODEV;	}	if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {		snd_printk(KERN_ERR "hda_codec: cannot malloc\n");		snd_hda_codec_free(codec);		return -ENOMEM;	}	if (!codec->subsystem_id) {		hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;		codec->subsystem_id =			snd_hda_codec_read(codec, nid, 0,					   AC_VERB_GET_SUBSYSTEM_ID, 0);	}	codec->preset = find_codec_preset(codec);	/* audio codec should override the mixer name */	if (codec->afg || !*bus->card->mixername)		snd_hda_get_codec_name(codec, bus->card->mixername,				       sizeof(bus->card->mixername));	if (is_generic_config(codec)) {		err = snd_hda_parse_generic_codec(codec);		goto patched;	}	if (codec->preset && codec->preset->patch) {		err = codec->preset->patch(codec);		goto patched;	}	/* call the default parser */	err = snd_hda_parse_generic_codec(codec);	if (err < 0)		printk(KERN_ERR "hda-codec: No codec parser is available\n"); patched:	if (err < 0) {		snd_hda_codec_free(codec);		return err;	}	if (codec->patch_ops.unsol_event)		init_unsol_queue(bus);	snd_hda_codec_proc_new(codec);#ifdef CONFIG_SND_HDA_HWDEP	snd_hda_create_hwdep(codec);#endif	sprintf(component, "HDA:%08x", codec->vendor_id);	snd_component_add(codec->bus->card, component);	if (codecp)		*codecp = codec;	return 0;}/** * snd_hda_codec_setup_stream - set up the codec for streaming * @codec: the CODEC to set up * @nid: the NID to set up * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. * @channel_id: channel id to pass, zero based. * @format: stream format. */void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,				u32 stream_tag,				int channel_id, int format){	if (!nid)		return;	snd_printdd("hda_codec_setup_stream: "		    "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",		    nid, stream_tag, channel_id, format);	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,			    (stream_tag << 4) | channel_id);	msleep(1);	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);}/* * amp access functions *//* FIXME: more better hash key? */#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))#define INFO_AMP_CAPS	(1<<0)#define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))/* initialize the hash table */static void __devinit init_hda_cache(struct hda_cache_rec *cache,				     unsigned int record_size){	memset(cache, 0, sizeof(*cache));	memset(cache->hash, 0xff, sizeof(cache->hash));	cache->record_size = record_size;}static void free_hda_cache(struct hda_cache_rec *cache){	kfree(cache->buffer);}/* query the hash.  allocate an entry if not found. */static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,					      u32 key){	u16 idx = key % (u16)ARRAY_SIZE(cache->hash);	u16 cur = cache->hash[idx];	struct hda_cache_head *info;	while (cur != 0xffff) {		info = (struct hda_cache_head *)(cache->buffer +						 cur * cache->record_size);		if (info->key == key)			return info;		cur = info->next;	}	/* add a new hash entry */	if (cache->num_entries >= cache->size) {		/* reallocate the array */		unsigned int new_size = cache->size + 64;		void *new_buffer;		new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);		if (!new_buffer) {			snd_printk(KERN_ERR "hda_codec: "				   "can't malloc amp_info\n");			return NULL;		}		if (cache->buffer) {			memcpy(new_buffer, cache->buffer,			       cache->size * cache->record_size);			kfree(cache->buffer);		}		cache->size = new_size;		cache->buffer = new_buffer;	}	cur = cache->num_entries++;	info = (struct hda_cache_head *)(cache->buffer +					 cur * cache->record_size);	info->key = key;	info->val = 0;	info->next = cache->hash[idx];	cache->hash[idx] = cur;	return info;}/* query and allocate an amp hash entry */static inline struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key){	return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);}/* * query AMP capabilities for the given widget and direction */static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction){	struct hda_amp_info *info;	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));	if (!info)		return 0;	if (!(info->head.val & INFO_AMP_CAPS)) {		if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))			nid = codec->afg;		info->amp_caps = snd_hda_param_read(codec, nid,						    direction == HDA_OUTPUT ?						    AC_PAR_AMP_OUT_CAP :						    AC_PAR_AMP_IN_CAP);		if (info->amp_caps)			info->head.val |= INFO_AMP_CAPS;	}	return info->amp_caps;}int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,			      unsigned int caps){	struct hda_amp_info *info;	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));	if (!info)		return -EINVAL;	info->amp_caps = caps;	info->head.val |= INFO_AMP_CAPS;	return 0;}/* * read the current volume to info * if the cache exists, read the cache value. */static unsigned int get_vol_mute(struct hda_codec *codec,				 struct hda_amp_info *info, hda_nid_t nid,				 int ch, int direction, int index){	u32 val, parm;	if (info->head.val & INFO_AMP_VOL(ch))		return info->vol[ch];	parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;	parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;	parm |= index;	val = snd_hda_codec_read(codec, nid, 0,				 AC_VERB_GET_AMP_GAIN_MUTE, parm);	info->vol[ch] = val & 0xff;	info->head.val |= INFO_AMP_VOL(ch);	return info->vol[ch];}/* * write the current volume in info to the h/w and update the cache */static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,			 hda_nid_t nid, int ch, int direction, int index,			 int val){	u32 parm;	parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;	parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;	parm |= index << AC_AMP_SET_INDEX_SHIFT;	parm |= val;	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);	info->vol[ch] = val;}/* * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit. */int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,			   int direction, int index){	struct hda_amp_info *info;	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));	if (!info)		return 0;	return get_vol_mute(codec, info, nid, ch, direction, index);}/* * update the AMP value, mask = bit mask to set, val = the value */int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,			     int direction, int idx, int mask, int val){	struct hda_amp_info *info;	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));	if (!info)		return 0;	val &= mask;	val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;	if (info->vol[ch] == val)		return 0;	put_vol_mute(codec, info, nid, ch, direction, idx, val);	return 1;}/* * update the AMP stereo with the same mask and value */int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,			     int direction, int idx, int mask, int val){	int ch, ret = 0;	for (ch = 0; ch < 2; ch++)		ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,						idx, mask, val);	return ret;}#ifdef SND_HDA_NEEDS_RESUME/* resume the all amp commands from the cache */void snd_hda_codec_resume_amp(struct hda_codec *codec){	struct hda_amp_info *buffer = codec->amp_cache.buffer;	int i;	for (i = 0; i < codec->amp_cache.size; i++, buffer++) {		u32 key = buffer->head.key;		hda_nid_t nid;		unsigned int idx, dir, ch;		if (!key)			continue;		nid = key & 0xff;		idx = (key >> 16) & 0xff;		dir = (key >> 24) & 0xff;		for (ch = 0; ch < 2; ch++) {			if (!(buffer->head.val & INFO_AMP_VOL(ch)))				continue;			put_vol_mute(codec, buffer, nid, ch, dir, idx,				     buffer->vol[ch]);		}	}}#endif /* SND_HDA_NEEDS_RESUME *//* * AMP control callbacks *//* retrieve parameters from private_value */#define get_amp_nid(kc)		((kc)->private_value & 0xffff)#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)/* volume */int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,				  struct snd_ctl_elem_info *uinfo){	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);	u16 nid = get_amp_nid(kcontrol);	u8 chs = get_amp_channels(kcontrol);	int dir = get_amp_direction(kcontrol);	u32 caps;	caps = query_amp_caps(codec, nid, dir);	/* num steps */	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;	if (!caps) {		printk(KERN_WARNING "hda_codec: "		       "num_steps = 0 for NID=0x%x\n", nid);		return -EINVAL;	}	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = chs == 3 ? 2 : 1;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = caps;	return 0;}int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,				 struct snd_ctl_elem_value *ucontrol){	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);	hda_nid_t nid = get_amp_nid(kcontrol);	int chs = get_amp_channels(kcontrol);	int dir = get_amp_direction(kcontrol);	int idx = get_amp_index(kcontrol);	long *valp = ucontrol->value.integer.value;	if (chs & 1)		*valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx)			& HDA_AMP_VOLMASK;	if (chs & 2)		*valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx)			& HDA_AMP_VOLMASK;	return 0;}int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,				 struct snd_ctl_elem_value *ucontrol){	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);	hda_nid_t nid = get_amp_nid(kcontrol);	int chs = get_amp_channels(kcontrol);	int dir = get_amp_direction(kcontrol);	int idx = get_amp_index(kcontrol);	long *valp = ucontrol->value.integer.value;	int change = 0;	snd_hda_power_up(codec);	if (chs & 1) {		change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,						  0x7f, *valp);		valp++;	}	if (chs & 2)		change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,						   0x7f, *valp);	snd_hda_power_down(codec);	return change;}int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,			  unsigned int size, unsigned int __user *_tlv){	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);

⌨️ 快捷键说明

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