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

📄 uda1341.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
字号:
/* * Philips UDA1341 mixer device driver * * Copyright (c) 2000 Nicolas Pitre <nico@cam.org> * * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. * * History: * * 2000-05-21	Nicolas Pitre	Initial release. * * 2000-08-19	Erik Bunce	More inline w/ OSS API and UDA1341 docs * 				including fixed AGC and audio source handling * * 2000-11-30	Nicolas Pitre	- More mixer functionalities. * * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on * 				the former sa1100-uda1341.c driver. * * 2001-08-13	Russell King	Re-written as part of the L3 interface */#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/soundcard.h>#include <linux/l3/l3.h>#include <linux/l3/uda1341.h>#include <asm/uaccess.h>#define DEF_VOLUME	65/* * UDA1341 L3 address and command types */#define UDA1341_L3ADDR		5#define UDA1341_DATA0		(UDA1341_L3ADDR << 2 | 0)#define UDA1341_DATA1		(UDA1341_L3ADDR << 2 | 1)#define UDA1341_STATUS		(UDA1341_L3ADDR << 2 | 2)struct uda1341_regs {	unsigned char	stat0;#define STAT0			0x00#define STAT0_RST		(1 << 6)#define STAT0_SC_MASK		(3 << 4)#define STAT0_SC_512FS		(0 << 4)#define STAT0_SC_384FS		(1 << 4)#define STAT0_SC_256FS		(2 << 4)#define STAT0_IF_MASK		(7 << 1)#define STAT0_IF_I2S		(0 << 1)#define STAT0_IF_LSB16		(1 << 1)#define STAT0_IF_LSB18		(2 << 1)#define STAT0_IF_LSB20		(3 << 1)#define STAT0_IF_MSB		(4 << 1)#define STAT0_IF_LSB16MSB	(5 << 1)#define STAT0_IF_LSB18MSB	(6 << 1)#define STAT0_IF_LSB20MSB	(7 << 1)#define STAT0_DC_FILTER		(1 << 0)	unsigned char	stat1;#define STAT1			0x80#define STAT1_DAC_GAIN		(1 << 6)	/* gain of DAC */#define STAT1_ADC_GAIN		(1 << 5)	/* gain of ADC */#define STAT1_ADC_POL		(1 << 4)	/* polarity of ADC */#define STAT1_DAC_POL		(1 << 3)	/* polarity of DAC */#define STAT1_DBL_SPD		(1 << 2)	/* double speed playback */#define STAT1_ADC_ON		(1 << 1)	/* ADC powered */#define STAT1_DAC_ON		(1 << 0)	/* DAC powered */	unsigned char	data0_0;#define DATA0			0x00#define DATA0_VOLUME_MASK	0x3f#define DATA0_VOLUME(x)		(x)	unsigned char	data0_1;#define DATA1			0x40#define DATA1_BASS(x)		((x) << 2)#define DATA1_BASS_MASK		(15 << 2)#define DATA1_TREBLE(x)		((x))#define DATA1_TREBLE_MASK	(3)	unsigned char	data0_2;#define DATA2			0x80#define DATA2_PEAKAFTER		(1 << 5)#define DATA2_DEEMP_NONE	(0 << 3)#define DATA2_DEEMP_32KHz	(1 << 3)#define DATA2_DEEMP_44KHz	(2 << 3)#define DATA2_DEEMP_48KHz	(3 << 3)#define DATA2_MUTE		(1 << 2)#define DATA2_FILTER_FLAT	(0 << 0)#define DATA2_FILTER_MIN	(1 << 0)#define DATA2_FILTER_MAX	(3 << 0)#define EXTADDR(n)		(0xc0 | (n))#define EXTDATA(d)		(0xe0 | (d))	unsigned char	ext0;#define EXT0			0#define EXT0_CH1_GAIN(x)	(x)	unsigned char	ext1;#define EXT1			1#define EXT1_CH2_GAIN(x)	(x)	unsigned char	ext2;#define EXT2			2#define EXT2_MIC_GAIN_MASK	(7 << 2)#define EXT2_MIC_GAIN(x)	((x) << 2)#define EXT2_MIXMODE_DOUBLEDIFF	(0)#define EXT2_MIXMODE_CH1	(1)#define EXT2_MIXMODE_CH2	(2)#define EXT2_MIXMODE_MIX	(3)	unsigned char	ext4;#define EXT4			4#define EXT4_AGC_ENABLE		(1 << 4)#define EXT4_INPUT_GAIN_MASK	(3)#define EXT4_INPUT_GAIN(x)	((x) & 3)	unsigned char	ext5;#define EXT5			5#define EXT5_INPUT_GAIN(x)	((x) >> 2)	unsigned char	ext6;#define EXT6			6#define EXT6_AGC_CONSTANT_MASK	(7 << 2)#define EXT6_AGC_CONSTANT(x)	((x) << 2)#define EXT6_AGC_LEVEL_MASK	(3)#define EXT6_AGC_LEVEL(x)	(x)};#define REC_MASK	(SOUND_MASK_LINE | SOUND_MASK_MIC)#define DEV_MASK	(REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)struct uda1341 {	struct uda1341_regs regs;	int		active;	unsigned short	volume;	unsigned short	bass;	unsigned short	treble;	unsigned short	line;	unsigned short	mic;	int		mod_cnt;};#define ADD_FIELD(reg,field)				\		*p++ = reg | uda->regs.field#define ADD_EXTFIELD(reg,field)				\		*p++ = EXTADDR(reg);			\		*p++ = EXTDATA(uda->regs.field);static void uda1341_sync(struct l3_client *clnt){	struct uda1341 *uda = clnt->driver_data;	char buf[24], *p = buf;	ADD_FIELD(STAT0, stat0);	ADD_FIELD(STAT1, stat1);	if (p != buf)		l3_write(clnt, UDA1341_STATUS, buf, p - buf);	p = buf;	ADD_FIELD(DATA0, data0_0);	ADD_FIELD(DATA1, data0_1);	ADD_FIELD(DATA2, data0_2);	ADD_EXTFIELD(EXT0, ext0);	ADD_EXTFIELD(EXT1, ext1);	ADD_EXTFIELD(EXT2, ext2);	ADD_EXTFIELD(EXT4, ext4);	ADD_EXTFIELD(EXT5, ext5);	ADD_EXTFIELD(EXT6, ext6);	if (p != buf)		l3_write(clnt, UDA1341_DATA0, buf, p - buf);}static void uda1341_cmd_init(struct l3_client *clnt){	struct uda1341 *uda = clnt->driver_data;	char buf[2];	uda->active = 1;	buf[0] = uda->regs.stat0 | STAT0_RST;	buf[1] = uda->regs.stat0;	l3_write(clnt, UDA1341_STATUS, buf, 2);	/* resend all parameters */	uda1341_sync(clnt);}static int uda1341_configure(struct l3_client *clnt, struct uda1341_cfg *conf){	struct uda1341 *uda = clnt->driver_data;	int ret = 0;	uda->regs.stat0 &= ~(STAT0_SC_MASK | STAT0_IF_MASK);	switch (conf->fs) {	case 512: uda->regs.stat0 |= STAT0_SC_512FS;	break;	case 384: uda->regs.stat0 |= STAT0_SC_384FS;	break;	case 256: uda->regs.stat0 |= STAT0_SC_256FS;	break;	default:  ret = -EINVAL;			break;	}	switch (conf->format) {	case FMT_I2S:		uda->regs.stat0 |= STAT0_IF_I2S;	break;	case FMT_LSB16:		uda->regs.stat0 |= STAT0_IF_LSB16;	break;	case FMT_LSB18:		uda->regs.stat0 |= STAT0_IF_LSB18;	break;	case FMT_LSB20:		uda->regs.stat0 |= STAT0_IF_LSB20;	break;	case FMT_MSB:		uda->regs.stat0 |= STAT0_IF_MSB;	break;	case FMT_LSB16MSB:	uda->regs.stat0 |= STAT0_IF_LSB16MSB;	break;	case FMT_LSB18MSB:	uda->regs.stat0 |= STAT0_IF_LSB18MSB;	break;	case FMT_LSB20MSB:	uda->regs.stat0 |= STAT0_IF_LSB20MSB;	break;	}	if (ret == 0 && uda->active) {		char buf = uda->regs.stat0 | STAT0;		l3_write(clnt, UDA1341_STATUS, &buf, 1);	}	return ret;}static int uda1341_update_direct(struct l3_client *clnt, int cmd, void *arg){	struct uda1341 *uda = clnt->driver_data;	struct l3_gain *v = arg;	char newreg;	int val;	switch (cmd) {	case L3_SET_VOLUME: /* set volume.  val =  0 to 100 => 62 to 1 */		uda->regs.data0_0 = DATA0_VOLUME(62 - ((v->left * 61) / 100));		newreg = uda->regs.data0_0 | DATA0;		break;	case L3_SET_BASS:   /* set bass.    val = 50 to 100 => 0 to 12 */		val = v->left - 50;		if (val < 0)			val = 0;		uda->regs.data0_1 &= ~DATA1_BASS_MASK;		uda->regs.data0_1 |= DATA1_BASS((val * 12) / 50);		newreg = uda->regs.data0_1 | DATA1;		break;	case L3_SET_TREBLE: /* set treble.  val = 50 to 100 => 0 to 3 */		val = v->left - 50;		if (val < 0)			val = 0;		uda->regs.data0_1 &= ~DATA1_TREBLE_MASK;		uda->regs.data0_1 |= DATA1_TREBLE((val * 3) / 50);		newreg = uda->regs.data0_1 | DATA1;		break;	default:		return -EINVAL;	}			if (uda->active)		l3_write(clnt, UDA1341_DATA0, &newreg, 1);	return 0;}static int uda1341_update_indirect(struct l3_client *clnt, int cmd, void *arg){	struct uda1341 *uda = clnt->driver_data;	struct l3_gain *gain = arg;	struct l3_agc *agc = arg;	char buf[8], *p = buf;	int val, ret = 0;	switch (cmd) {	case L3_SET_GAIN:		val = 31 - (gain->left * 31 / 100);		switch (gain->channel) {		case 1:			uda->regs.ext0 = EXT0_CH1_GAIN(val);			ADD_EXTFIELD(EXT0, ext0);			break;		case 2:			uda->regs.ext1 = EXT1_CH2_GAIN(val);			ADD_EXTFIELD(EXT1, ext1);			break;		default:			ret = -EINVAL;		}		break;	case L3_INPUT_AGC:		if (agc->channel == 2) {			if (agc->enable)				uda->regs.ext4 |= EXT4_AGC_ENABLE;			else				uda->regs.ext4 &= ~EXT4_AGC_ENABLE;#if 0			agc->level			agc->attack			agc->decay#endif			ADD_EXTFIELD(EXT4, ext4);		} else			ret = -EINVAL;		break;	default:		ret = -EINVAL;		break;	}	if (ret == 0 && uda->active)		l3_write(clnt, UDA1341_DATA0, buf, p - buf);	return ret;}static int uda1341_mixer_ioctl(struct l3_client *clnt, int cmd, void *arg){	struct uda1341 *uda = clnt->driver_data;	struct l3_gain gain;	int val, nr = _IOC_NR(cmd), ret = 0;	if (cmd == SOUND_MIXER_INFO) {		struct mixer_info mi;		strncpy(mi.id, "UDA1341", sizeof(mi.id));		strncpy(mi.name, "Philips UDA1341", sizeof(mi.name));		mi.modify_counter = uda->mod_cnt;		return copy_to_user(arg, &mi, sizeof(mi));	}	if (_IOC_DIR(cmd) & _IOC_WRITE) {		ret = get_user(val, (int *)arg);		if (ret)			goto out;		gain.left    = val & 255;		gain.right   = val >> 8;		gain.channel = 0;		switch (nr) {		case SOUND_MIXER_VOLUME:			uda->volume = val;			uda->mod_cnt++;			uda1341_update_direct(clnt, L3_SET_VOLUME, &gain);			break;		case SOUND_MIXER_BASS:			uda->bass = val;			uda->mod_cnt++;			uda1341_update_direct(clnt, L3_SET_BASS, &gain);			break;		case SOUND_MIXER_TREBLE:			uda->treble = val;			uda->mod_cnt++;			uda1341_update_direct(clnt, L3_SET_TREBLE, &gain);			break;		case SOUND_MIXER_LINE:			uda->line = val;			gain.channel = 1;			uda->mod_cnt++;			uda1341_update_indirect(clnt, L3_SET_GAIN, &gain);			break;		case SOUND_MIXER_MIC:			uda->mic = val;			gain.channel = 2;			uda->mod_cnt++;			uda1341_update_indirect(clnt, L3_SET_GAIN, &gain);			break;		case SOUND_MIXER_RECSRC:			break;		default:			ret = -EINVAL;		}	}	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {		int nr = _IOC_NR(cmd);		ret = 0;		switch (nr) {		case SOUND_MIXER_VOLUME:     val = uda->volume;	break;		case SOUND_MIXER_BASS:       val = uda->bass;	break;		case SOUND_MIXER_TREBLE:     val = uda->treble;	break;		case SOUND_MIXER_LINE:       val = uda->line;	break;		case SOUND_MIXER_MIC:        val = uda->mic;	break;		case SOUND_MIXER_RECSRC:     val = REC_MASK;	break;		case SOUND_MIXER_RECMASK:    val = REC_MASK;	break;		case SOUND_MIXER_DEVMASK:    val = DEV_MASK;	break;		case SOUND_MIXER_CAPS:       val = 0;		break;		case SOUND_MIXER_STEREODEVS: val = 0;		break;		default:	val = 0;     ret = -EINVAL;	break;		}		if (ret == 0)			ret = put_user(val, (int *)arg);	}out:	return ret;}static int uda1341_attach(struct l3_client *clnt){	struct uda1341 *uda;	uda = kmalloc(sizeof(*uda), GFP_KERNEL);	if (!uda)		return -ENOMEM;	memset(uda, 0, sizeof(*uda));	uda->volume = DEF_VOLUME | DEF_VOLUME << 8;	uda->bass   = 50 | 50 << 8;	uda->treble = 50 | 50 << 8;	uda->line   = 88 | 88 << 8;	uda->mic    = 88 | 88 << 8;	uda->regs.stat0   = STAT0_SC_256FS | STAT0_IF_LSB16;	uda->regs.stat1   = STAT1_DAC_GAIN | STAT1_ADC_GAIN |			    STAT1_ADC_ON | STAT1_DAC_ON;	uda->regs.data0_0 = DATA0_VOLUME(62 - ((DEF_VOLUME * 61) / 100));	uda->regs.data0_1 = DATA1_BASS(0) | DATA1_TREBLE(0);	uda->regs.data0_2 = DATA2_PEAKAFTER | DATA2_DEEMP_NONE |			    DATA2_FILTER_MAX;	uda->regs.ext0    = EXT0_CH1_GAIN(4);	uda->regs.ext1    = EXT1_CH2_GAIN(4);	uda->regs.ext2    = EXT2_MIXMODE_MIX | EXT2_MIC_GAIN(4);	uda->regs.ext4    = EXT4_AGC_ENABLE | EXT4_INPUT_GAIN(0);	uda->regs.ext5    = EXT5_INPUT_GAIN(0);	uda->regs.ext6    = EXT6_AGC_CONSTANT(3) | EXT6_AGC_LEVEL(0);	clnt->driver_data = uda;	return 0;}static void uda1341_detach(struct l3_client *clnt){	kfree(clnt->driver_data);}static intuda1341_command(struct l3_client *clnt, int cmd, void *arg){	int ret = -EINVAL;	if (_IOC_TYPE(cmd) == 'M')		ret = uda1341_mixer_ioctl(clnt, cmd, arg);	else if (cmd == L3_UDA1341_CONFIGURE)		ret = uda1341_configure(clnt, arg);	return ret;}static int uda1341_open(struct l3_client *clnt){	uda1341_cmd_init(clnt);	return 0;}static void uda1341_close(struct l3_client *clnt){	struct uda1341 *uda = clnt->driver_data;	uda->active = 0;}static struct l3_ops uda1341_ops = {	open:		uda1341_open,	command:	uda1341_command,	close:		uda1341_close,};static struct l3_driver uda1341 = {	name:		UDA1341_NAME,	attach_client:	uda1341_attach,	detach_client:	uda1341_detach,	ops:		&uda1341_ops,	owner:		THIS_MODULE,};static int __init uda1341_init(void){	return l3_add_driver(&uda1341);}static void __exit uda1341_exit(void){	l3_del_driver(&uda1341);}module_init(uda1341_init);module_exit(uda1341_exit);MODULE_AUTHOR("Nicolas Pitre");MODULE_DESCRIPTION("Philips UDA1341 CODEC driver");

⌨️ 快捷键说明

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