ac97_codec.c

来自「linux 内核源代码」· C语言 代码 · 共 1,489 行 · 第 1/3 页

C
1,489
字号
	/* else, write the first set in the mask as the	   output */		/* clear out current set value first (AC97 supports only 1 input!) */	val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);	if (mask != val)	    mask &= ~val;       	val = ffs(mask); 	val = ac97_oss_rm[val-1];	val |= val << 8;  /* set both channels */#ifdef DEBUG	printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);#endif	codec->codec_write(codec, AC97_RECORD_SELECT, val);	return 0;};static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg){	int i, val = 0;	if (cmd == SOUND_MIXER_INFO) {		mixer_info info;		memset(&info, 0, sizeof(info));		strlcpy(info.id, codec->name, sizeof(info.id));		strlcpy(info.name, codec->name, sizeof(info.name));		info.modify_counter = codec->modcnt;		if (copy_to_user((void __user *)arg, &info, sizeof(info)))			return -EFAULT;		return 0;	}	if (cmd == SOUND_OLD_MIXER_INFO) {		_old_mixer_info info;		memset(&info, 0, sizeof(info));		strlcpy(info.id, codec->name, sizeof(info.id));		strlcpy(info.name, codec->name, sizeof(info.name));		if (copy_to_user((void __user *)arg, &info, sizeof(info)))			return -EFAULT;		return 0;	}	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))		return -EINVAL;	if (cmd == OSS_GETVERSION)		return put_user(SOUND_VERSION, (int __user *)arg);	if (_SIOC_DIR(cmd) == _SIOC_READ) {		switch (_IOC_NR(cmd)) {		case SOUND_MIXER_RECSRC: /* give them the current record source */			if (!codec->recmask_io) {				val = 0;			} else {				val = codec->recmask_io(codec, 1, 0);			}			break;		case SOUND_MIXER_DEVMASK: /* give them the supported mixers */			val = codec->supported_mixers;			break;		case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */			val = codec->record_sources;			break;		case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */			val = codec->stereo_mixers;			break;		case SOUND_MIXER_CAPS:			val = SOUND_CAP_EXCL_INPUT;			break;		default: /* read a specific mixer */			i = _IOC_NR(cmd);			if (!supported_mixer(codec, i)) 				return -EINVAL;			/* do we ever want to touch the hardware? */		        /* val = codec->read_mixer(codec, i); */			val = codec->mixer_state[i]; 			break;		}		return put_user(val, (int __user *)arg);	}	if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {		codec->modcnt++;		if (get_user(val, (int __user *)arg))			return -EFAULT;		switch (_IOC_NR(cmd)) {		case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */			if (!codec->recmask_io) return -EINVAL;			if (!val) return 0;			if (!(val &= codec->record_sources)) return -EINVAL;			codec->recmask_io(codec, 0, val);			return 0;		default: /* write a specific mixer */			i = _IOC_NR(cmd);			if (!supported_mixer(codec, i)) 				return -EINVAL;			ac97_set_mixer(codec, i, val);			return 0;		}	}	return -EINVAL;}/* entry point for /proc/driver/controller_vendor/ac97/%d */int ac97_read_proc (char *page, char **start, off_t off,		    int count, int *eof, void *data){	int len = 0, cap, extid, val, id1, id2;	struct ac97_codec *codec;	int is_ac97_20 = 0;	if ((codec = data) == NULL)		return -ENODEV;	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);	len += sprintf (page+len, "Vendor name      : %s\n", codec->name);	len += sprintf (page+len, "Vendor id        : %04X %04X\n", id1, id2);	extid = codec->codec_read(codec, AC97_EXTENDED_ID);	extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13));	len += sprintf (page+len, "AC97 Version     : %s\n",			extid ? "2.0 or later" : "1.0");	if (extid) is_ac97_20 = 1;	cap = codec->codec_read(codec, AC97_RESET);	len += sprintf (page+len, "Capabilities     :%s%s%s%s%s%s\n",			cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "",			cap & 0x0002 ? " -reserved1-" : "",			cap & 0x0004 ? " -bass & treble-" : "",			cap & 0x0008 ? " -simulated stereo-" : "",			cap & 0x0010 ? " -headphone out-" : "",			cap & 0x0020 ? " -loudness-" : "");	val = cap & 0x00c0;	len += sprintf (page+len, "DAC resolutions  :%s%s%s\n",			" -16-bit-",			val & 0x0040 ? " -18-bit-" : "",			val & 0x0080 ? " -20-bit-" : "");	val = cap & 0x0300;	len += sprintf (page+len, "ADC resolutions  :%s%s%s\n",			" -16-bit-",			val & 0x0100 ? " -18-bit-" : "",			val & 0x0200 ? " -20-bit-" : "");	len += sprintf (page+len, "3D enhancement   : %s\n",			ac97_stereo_enhancements[(cap >> 10) & 0x1f]);	val = codec->codec_read(codec, AC97_GENERAL_PURPOSE);	len += sprintf (page+len, "POP path         : %s 3D\n"			"Sim. stereo      : %s\n"			"3D enhancement   : %s\n"			"Loudness         : %s\n"			"Mono output      : %s\n"			"MIC select       : %s\n"			"ADC/DAC loopback : %s\n",			val & 0x8000 ? "post" : "pre",			val & 0x4000 ? "on" : "off",			val & 0x2000 ? "on" : "off",			val & 0x1000 ? "on" : "off",			val & 0x0200 ? "MIC" : "MIX",			val & 0x0100 ? "MIC2" : "MIC1",			val & 0x0080 ? "on" : "off");	extid = codec->codec_read(codec, AC97_EXTENDED_ID);	cap = extid;	len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n",			cap & 0x0001 ? " -var rate PCM audio-" : "",			cap & 0x0002 ? " -2x PCM audio out-" : "",			cap & 0x0008 ? " -var rate MIC in-" : "",			cap & 0x0040 ? " -PCM center DAC-" : "",			cap & 0x0080 ? " -PCM surround DAC-" : "",			cap & 0x0100 ? " -PCM LFE DAC-" : "",			cap & 0x0200 ? " -slot/DAC mappings-" : "");	if (is_ac97_20) {		len += sprintf (page+len, "Front DAC rate   : %d\n",				codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE));	}	return len;}/** *	codec_id	-  Turn id1/id2 into a PnP string *	@id1: Vendor ID1 *	@id2: Vendor ID2 *	@buf: CODEC_ID_BUFSZ byte buffer * *	Fills buf with a zero terminated PnP ident string for the id1/id2 *	pair. For convenience the return is the passed in buffer pointer. */ static char *codec_id(u16 id1, u16 id2, char *buf){	if(id1&0x8080) {		snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);	} else {		buf[0] = (id1 >> 8);		buf[1] = (id1 & 0xFF);		buf[2] = (id2 >> 8);		snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);	}	return buf;} /** *	ac97_check_modem - Check if the Codec is a modem *	@codec: codec to check * *	Return true if the device is an AC97 1.0 or AC97 2.0 modem */ static int ac97_check_modem(struct ac97_codec *codec){	/* Check for an AC97 1.0 soft modem (ID1) */	if(codec->codec_read(codec, AC97_RESET) & 2)		return 1;	/* Check for an AC97 2.x soft modem */	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);	if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)		return 1;	return 0;}/** *	ac97_alloc_codec - Allocate an AC97 codec * *	Returns a new AC97 codec structure. AC97 codecs may become *	refcounted soon so this interface is needed. Returns with *	one reference taken. */ struct ac97_codec *ac97_alloc_codec(void){	struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);	if(!codec)		return NULL;	spin_lock_init(&codec->lock);	INIT_LIST_HEAD(&codec->list);	return codec;}EXPORT_SYMBOL(ac97_alloc_codec);/** *	ac97_release_codec -	Release an AC97 codec *	@codec: codec to release * *	Release an allocated AC97 codec. This will be refcounted in *	time but for the moment is trivial. Calls the unregister *	handler if the codec is now defunct. */ void ac97_release_codec(struct ac97_codec *codec){	/* Remove from the list first, we don't want to be	   "rediscovered" */	mutex_lock(&codec_mutex);	list_del(&codec->list);	mutex_unlock(&codec_mutex);	/*	 *	The driver needs to deal with internal	 *	locking to avoid accidents here. 	 */	if(codec->driver)		codec->driver->remove(codec, codec->driver);	kfree(codec);}EXPORT_SYMBOL(ac97_release_codec);/** *	ac97_probe_codec - Initialize and setup AC97-compatible codec *	@codec: (in/out) Kernel info for a single AC97 codec * *	Reset the AC97 codec, then initialize the mixer and *	the rest of the @codec structure. * *	The codec_read and codec_write fields of @codec are *	required to be setup and working when this function *	is called.  All other fields are set by this function. * *	codec_wait field of @codec can optionally be provided *	when calling this function.  If codec_wait is not %NULL, *	this function will call codec_wait any time it is *	necessary to wait for the audio chip to reach the *	codec-ready state.  If codec_wait is %NULL, then *	the default behavior is to call schedule_timeout. *	Currently codec_wait is used to wait for AC97 codec *	reset to complete.  * *     Some codecs will power down when a register reset is *     performed. We now check for such codecs. * *	Returns 1 (true) on success, or 0 (false) on failure. */ int ac97_probe_codec(struct ac97_codec *codec){	u16 id1, id2;	u16 audio;	int i;	char cidbuf[CODEC_ID_BUFSZ];	u16 f;	struct list_head *l;	struct ac97_driver *d;		/* wait for codec-ready state */	if (codec->codec_wait)		codec->codec_wait(codec);	else		udelay(10);	/* will the codec power down if register reset ? */	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);	codec->name = NULL;	codec->codec_ops = &null_ops;	for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {			codec->type = ac97_codec_ids[i].id;			codec->name = ac97_codec_ids[i].name;			codec->codec_ops = ac97_codec_ids[i].ops;			codec->flags = ac97_codec_ids[i].flags;			break;		}	}	codec->model = (id1 << 16) | id2;	if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {		/* reset codec and wait for the ready bit before we continue */		codec->codec_write(codec, AC97_RESET, 0L);		if (codec->codec_wait)			codec->codec_wait(codec);		else			udelay(10);	}	/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should	 * be read zero.	 *	 * FIXME: is the following comment outdated?  -jgarzik	 * Probing of AC97 in this way is not reliable, it is not even SAFE !!	 */	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",		       (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")		       : (codec->id&1 ? "Secondary":  "Primary"));		return 0;	}		/* probe for Modem Codec */	codec->modem = ac97_check_modem(codec);	/* enable SPDIF */	f = codec->codec_read(codec, AC97_EXTENDED_STATUS);	if((codec->codec_ops == &null_ops) && (f & 4))		codec->codec_ops = &default_digital_ops;		/* A device which thinks its a modem but isnt */	if(codec->flags & AC97_DELUDED_MODEM)		codec->modem = 0;			if (codec->name == NULL)		codec->name = "Unknown";	printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n", 		codec->modem ? "Modem" : (audio ? "Audio" : ""),	       codec_id(id1, id2, cidbuf), codec->name);	if(!ac97_init_mixer(codec))		return 0;			/* 	 *	Attach last so the caller can override the mixer	 *	callbacks.	 */	 	mutex_lock(&codec_mutex);	list_add(&codec->list, &codecs);	list_for_each(l, &codec_drivers) {		d = list_entry(l, struct ac97_driver, list);		if ((codec->model ^ d->codec_id) & d->codec_mask)			continue;		if(d->probe(codec, d) == 0)		{			codec->driver = d;			break;		}	}	mutex_unlock(&codec_mutex);	return 1;}static int ac97_init_mixer(struct ac97_codec *codec){	u16 cap;	int i;	cap = codec->codec_read(codec, AC97_RESET);	/* mixer masks */	codec->supported_mixers = AC97_SUPPORTED_MASK;	codec->stereo_mixers = AC97_STEREO_MASK;	codec->record_sources = AC97_RECORD_MASK;	if (!(cap & 0x04))		codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);	if (!(cap & 0x10))		codec->supported_mixers &= ~SOUND_MASK_ALTPCM;	/* detect bit resolution */	codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);	if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)		codec->bit_resolution = 6;	else		codec->bit_resolution = 5;	/* generic OSS to AC97 wrapper */	codec->read_mixer = ac97_read_mixer;	codec->write_mixer = ac97_write_mixer;	codec->recmask_io = ac97_recmask_io;	codec->mixer_ioctl = ac97_mixer_ioctl;	/* initialize mixer channel volumes */	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {		struct mixer_defaults *md = &mixer_defaults[i];		if (md->mixer == -1) 			break;		if (!supported_mixer(codec, md->mixer)) 			continue;		ac97_set_mixer(codec, md->mixer, md->value);	}	/* codec specific initialization for 4-6 channel output or secondary codec stuff */	if (codec->codec_ops->init != NULL) {		codec->codec_ops->init(codec);	}	/*	 *	Volume is MUTE only on this device. We have to initialise	 *	it but its useless beyond that.	 */	if(codec->flags & AC97_NO_PCM_VOLUME)	{		codec->supported_mixers &= ~SOUND_MASK_PCM;		printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");	}	return 1;}#define AC97_SIGMATEL_ANALOG    0x6c	/* Analog Special */#define AC97_SIGMATEL_DAC2INVERT 0x6e#define AC97_SIGMATEL_BIAS1     0x70#define AC97_SIGMATEL_BIAS2     0x72#define AC97_SIGMATEL_MULTICHN  0x74	/* Multi-Channel programming */#define AC97_SIGMATEL_CIC1      0x76#define AC97_SIGMATEL_CIC2      0x78static int sigmatel_9708_init(struct ac97_codec * codec){	u16 codec72, codec6c;	codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;	codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);	if ((codec72==0) && (codec6c==0)) {		codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);		codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);		codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);		codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);	} else if ((codec72==0x8000) && (codec6c==0)) {		codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);		codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);		codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);	} else if ((codec72==0x8000) && (codec6c==0x0080)) {		/* nothing */	}	codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);

⌨️ 快捷键说明

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