tsc2101.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 489 行

C
489
字号
/********* linux/sound/arm/tsc2101.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 * 2004-03-23	Pavana Sharma	Re-written for TSC2101. *//************************************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/err.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/control.h>#include <asm/io.h>#include "tsc_2101.h"#include <asm/arch/omap24xx_spi.h>/*************** Defines ******************/#define SPI0 0#define MCSPI_INTERFACE#define PAGE2_AUDIO_CODEC_REGISTERS  2 << 11#define LEAVE_CS 0x80#define DEFAULT_VOLUME         93 #define DEFAULT_INPUT_VOLUME     0	/* 0 ==> mute line in */					/* use input vol of 75 for 0dB gain */#define OUTPUT_VOLUME_MIN 0x7F#define OUTPUT_VOLUME_MAX 0x00#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MIN#define MCBSP_I2S_MASTER#define AUDIO_RATE_DEFAULT	44100/*--------Debug prints--------------*///#define TEST#undef TEST#ifdef TEST#define DPRINTK(fmt, args...) printk(KERN_INFO "\n  %s: %s: " fmt,__FILE__,__FUNCTION__ , ## args)#else#define DPRINTK(fmt, args...)#endif/*----------------------------------*//*************************************************//********Local definitions *********************/extern u16 omap1610_uwire_data_transfer(u8 cs, u16 data, u8 trans_size, u8 rec_size);extern void omap1610_uwire_cs1_configure_mode (u8 edge_rd, u8 edge_wr, u8 lvl, u8 frq, u8 chk);static long audio_samplerate = AUDIO_RATE_DEFAULT;/*********************************************//*struct omap_samplerate_info{        unsigned int fs;        unsigned int clkdiv;        u16 divisor;        u16 fs_44kHz;        u16 pll0;        u16 pll1;};*/struct Tsc2101_regs {/* Structure which contains the registers on Tsc2101 */u16 reg[38];};struct Tsc2101{	spinlock_t	lock; /* spin lock for exclusive access to registers */	int		active;/* to specify that the controller is active */struct Tsc2101_regs	regs;/* Tsc2101 register set */};struct tsc2101_ctl {	const char *name;	snd_kcontrol_new_t *ctl;	u16 min;	u16 max;	u16 def;	u16 reg;	u16 mask;	u16 off;};#ifdef UWIRE_INTERFACEu16 tsc2101_read(u8 address){	u16 tmp1;	omap1610_uwire_data_transfer(LEAVE_CS | 1, (0x8000 | PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)), 16, 0);	tmp1= omap1610_uwire_data_transfer(1, (PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)), 0, 16);	DPRINTK("val = 0x%x",tmp1);	return tmp1;}void tsc2101_write(u8 address, u16 data){	omap1610_uwire_data_transfer(LEAVE_CS | 1, (PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)), 16, 0);	omap1610_uwire_data_transfer(1, data, 16, 0);	DPRINTK("val=0x%x",data);}#endif#ifdef MCSPI_INTERFACEu32 tsc2101_read(u8 address){        u32 data;	DPRINTK("addr = %x",address);        omap24xx_spi_enablechannel(SPI0);        /* write command word */        omap24xx_spi_writetochannel(SPI0, (0x8000 | PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)));        data = omap24xx_spi_readfromchannel(SPI0);        omap24xx_spi_disablechannel(SPI0);        DPRINTK("read : addr = %x, data = %x", address, data);        	return data;}void tsc2101_write(u8 address, u32 data){	DPRINTK("write : addr = %x, data = %x", address, data);        omap24xx_spi_enablechannel(SPI0);        /* write command word */        omap24xx_spi_writetochannel(SPI0, (PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)));        omap24xx_spi_writetochannel(SPI0, data);        omap24xx_spi_disablechannel(SPI0);}#endifstatic void tsc2101_program(struct Tsc2101 *tsc){	u16 volume;	/*----------Init_codec --------*/	DPRINTK("Init codec");		tsc2101_write(TSC2101_CODEC_POWER_CTRL,0);	tsc2101_write(TSC2101_HEADSET_GAIN_CTRL, 0x8000);//test	tsc2101_write(TSC2101_HANDSET_GAIN_CTRL, 0x0000);	tsc2101_write(TSC2101_AGC_CTRL, 0x0000);	tsc2101_write(TSC2101_BUZZER_GAIN_CTRL, 0xC57C);	tsc2101_write(TSC2101_MIXER_PGA_CTRL, 0x8030);	tsc2101_write(TSC2101_AUDIO_CTRL_2, 0x4410);	tsc2101_write(TSC2101_AUDIO_CTRL_4, 0x4540);	tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000);	tsc2101_write(TSC2101_AUDIO_CTRL_5, 0x4080);	tsc2101_write(TSC2101_AUDIO_CTRL_6, 0xC048);	tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);	  volume = ((DEFAULT_VOLUME * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;  	  tsc2101_write(TSC2101_DAC_GAIN_CTRL,(volume<<8)|volume);	DPRINTK("In tsc2101_program");	/*---------End Init_codec ---------*/}void tsc2101_configure(struct omap_samplerate_info *conf){		struct omap_samplerate_info *sr_conf;	sr_conf = conf;	tsc2101_write(TSC2101_AUDIO_CTRL_3, (sr_conf->fs_44kHz<<13)|(1<<11));	tsc2101_write(TSC2101_AUDIO_CTRL_1, (sr_conf->divisor<<3)|sr_conf->divisor);	tsc2101_write(TSC2101_PLL_PROG_1, sr_conf->pll0);	tsc2101_write(TSC2101_PLL_PROG_2, sr_conf->pll1); 	DPRINTK("configure PLL");}static int tsc2101_ctl_simple_info(snd_kcontrol_t *kc, snd_ctl_elem_info_t *ei){  struct tsc2101_ctl *ctl = (struct tsc2101_ctl *)kc->private_value;	ei->type = ctl->mask > 1 ? SNDRV_CTL_ELEM_TYPE_INTEGER :				  SNDRV_CTL_ELEM_TYPE_BOOLEAN;	ei->count = 1;	ei->value.integer.min = ctl->min;	ei->value.integer.max = ctl->max;	DPRINTK("Get control info");	return 0;}static int tsc2101_ctl_simple_get(snd_kcontrol_t *kc, snd_ctl_elem_value_t *ev){	struct Tsc2101 *tsc = kc->private_data;	struct tsc2101_ctl *ctl = (struct tsc2101_ctl *)kc->private_value;	unsigned int val;	val = tsc->regs.reg[ctl->reg] & ctl->mask;	DPRINTK("val1 =0x%x",val);	ev->value.integer.value[0] = val;	DPRINTK("get control val= 0x%x ; %d",val,val);	return 0;}static int tsc2101_ctl_simple_put(snd_kcontrol_t *kc, snd_ctl_elem_value_t *ev){	u16 temp;	struct Tsc2101 *tsc = kc->private_data;	struct tsc2101_ctl *ctl = (struct tsc2101_ctl *)kc->private_value;	u16 val = (ev->value.integer.value[0]<<ctl->off);	DPRINTK("ev= 0x%x ; %d val=0x%x;%d",ev->value.integer.value[0] ,ev->value.integer.value[0],val,val);	tsc->regs.reg[ctl->reg] = tsc2101_read(ctl->reg);	DPRINTK("reg = 0x%x", tsc->regs.reg[ctl->reg]);	DPRINTK("reg = 0x%x ; ~ctl_mask = 0x%x",tsc->regs.reg[ctl->reg],(~(ctl->mask)));	spin_lock(&tsc->lock);	tsc->regs.reg[ctl->reg] &= ~(ctl->mask);	DPRINTK("tsc->regs.reg[ctl->reg] =0x%x after mask ",tsc->regs.reg[ctl->reg]);	tsc->regs.reg[ctl->reg] |= val ;	DPRINTK("tsc->regs.reg[ctl->reg]=0x%x updated val",tsc->regs.reg[ctl->reg]);	spin_unlock(&tsc->lock);	if (tsc->active) {  /*Comment for testing; Uncomment if() later*/	tsc2101_write(ctl->reg,tsc->regs.reg[ctl->reg]);		temp = tsc2101_read(ctl->reg);	DPRINTK("temp = 0x%x",temp);	}	DPRINTK("Put control val =0x%x ; %d ",tsc->regs.reg[ctl->reg],tsc->regs.reg[ctl->reg]);	return 0;}static snd_kcontrol_new_t tsc2101_ctl_simple = {	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,	.info	= tsc2101_ctl_simple_info,	.get	= tsc2101_ctl_simple_get,	.put	= tsc2101_ctl_simple_put,};static struct tsc2101_ctl tsc2101_controls[] = {	{		.name	= "omap Master Playback Volume",		.ctl	= &tsc2101_ctl_simple,		.min	= 0,		.max	= 127, 		.def	= DEFAULT_VOLUME,		.reg	= TSC2101_DAC_GAIN_CTRL,		.mask	= 0x7f,		.off	= 0,	},	{		.name	= "omap Line Capture Volume",		.ctl	= &tsc2101_ctl_simple,		.min	= 1,		.max	= 0,		.def	= 1,		.reg	= TSC2101_HEADSET_GAIN_CTRL,		.mask	= 0x8000,		.off	= 15,	},	{		.name	= "omap Mic Capture Volume",		.ctl	= &tsc2101_ctl_simple,		.min	= 1,		.max	= 0,		.def	= 1,		.reg	= TSC2101_HEADSET_GAIN_CTRL,		.mask	= 0x8000,		.off	= 15,	},	{                .name   = "Master Playback Switch",                .ctl    = &tsc2101_ctl_simple,                .min    = 1,                .max    = 0,                .def    = 0,                .reg    = TSC2101_DAC_GAIN_CTRL,                .mask   = 0x8080,                .off    = 7,        },	{                .name   = "Codec Power Switch",                .ctl    = &tsc2101_ctl_simple,                .min    = 1,                .max    = 0,                .def    = 0,                .reg    = TSC2101_CODEC_POWER_CTRL,                .mask   = 0xffff,                .off    = 0,        }};static int tsc2101_add_ctl(snd_card_t *card, struct Tsc2101 *tsc, struct tsc2101_ctl *tscctl){	snd_kcontrol_t *ctl;	int ret = -ENOMEM;	ctl = snd_ctl_new1(tscctl->ctl, tsc);	if (ctl) {		strlcpy(ctl->id.name, tscctl->name, sizeof(ctl->id.name));		ctl->private_value = (unsigned long)tscctl;		tsc->regs.reg[tscctl->reg] = tsc2101_read(tscctl->reg);		tsc->regs.reg[tscctl->reg] &= ~(tscctl->mask << tscctl->off);		tsc->regs.reg[tscctl->reg] |= tscctl->def << tscctl->off;		DPRINTK("\n before tsc2101_write");		tsc2101_write(tscctl->reg,tsc->regs.reg[tscctl->reg]);					ret = snd_ctl_add(card, ctl);		DPRINTK("Add control %s", ctl->id.name);		if (ret < 0)			snd_ctl_free_one(ctl);	}	return ret;}static int tsc2101_free(snd_device_t *dev){	/* frees the Tsc2101 structure */	struct Tsc2101 *tsc = dev->device_data;	kfree(tsc);	DPRINTK("Free tsc2101 struct");	omap24xx_spi1_exit();	return 0;}static snd_device_ops_t tsc2101_ops = {	.dev_free = tsc2101_free,};void tsc2101_spi_init(){        struct spi_channel_config ch0_config;        omap24xx_spi1_init();       #if 1 	ch0_config.mode = MASTER;        ch0_config.endianess = MCSPI_MODULCTRL_LITTLEEND; /* little endian */        ch0_config.transmitreceive = MCSPI_CHCONF_TRANSRECEIVE;        ch0_config.wordlength = MCSPI_CHCONF_WL16;        ch0_config.spipolarity =MCSPI_CHCONF_EPOL_LOW ;        ch0_config.clkphase = MCSPI_CHCONF_PHA_ODD;        ch0_config.clkpolarity = MCSPI_CHCONF_POL_HIGH;        ch0_config.clkdivisor = MCSPI_CHCONF_CLKD_1;        omap24xx_spi_channelconfig(SPI0, &ch0_config);#endif        /* make irqenable as 0 : TODO */}struct Tsc2101 *tsc2101_create(snd_card_t *card){/*creates a structure tsc of type struct tsc2101. * creates a new sound device with the struct tsc & tsc2101_ops. * Initilizes the registers of Tsc2101_regs  using default values. * These values are written to the controller using tsc2101_write.	 * Adds the sound controls by calling function tsc2101_add_ctl().*/	struct Tsc2101 *tsc;	int i, ret;	u16 volume;	tsc = kmalloc(sizeof(*tsc), GFP_KERNEL);	if (!tsc)		return ERR_PTR(-ENOMEM);	memset(tsc, 0, sizeof(*tsc));	ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, tsc, &tsc2101_ops);	if (ret)		goto err;	strlcpy(card->mixername, "TSC2101", sizeof(card->mixername));#ifdef UWIRE_INTERFACE	outl(inl(PU_PD_SEL_2)|(1<<22), PU_PD_SEL_2); //Configure MCLK enable	outl(inl(FUNC_MUX_CTRL_7)|(2<<18), FUNC_MUX_CTRL_7); //Configure N15 pin to be uWire CS1	omap1610_uwire_cs1_configure_mode(1,1,0,2,0);#endif	DPRINTK("created new sound device & configured");		/****Init_codec ****/	tsc2101_spi_init();        DPRINTK("tsc2101 spi init done");	strlcpy(card->mixername, "TSC2101", sizeof(card->mixername));	for (i = 0; i < ARRAY_SIZE(tsc2101_controls); i++) {		ret = tsc2101_add_ctl(card, tsc, &tsc2101_controls[i]);		DPRINTK("Added control%d",i);		if (ret)			goto err;	}		/* Mute Cellphone In, Buzzer In */	tsc2101_write(TSC2101_BUZZER_GAIN_CTRL, 0xC57C);	/*Mute Analog Sidetone	 *Select MIC_INHND input for handset mic	 *Cell Phone In not connected */	tsc2101_write(TSC2101_MIXER_PGA_CTRL, 0x8030);	/*Keyclick disabled	 *Keyclick medium amplitude, 1kHz, 4 periods keyclick */	tsc2101_write(TSC2101_AUDIO_CTRL_2, 0x4410);	/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled	 * 4dB AGC hysteresis	 MICes bias 2V */	tsc2101_write(TSC2101_AUDIO_CTRL_4, 0x4540);	/* DAC left routed to SPK2, right to SPK1	 SPK1/2 unmuted */	tsc2101_write(TSC2101_AUDIO_CTRL_5, 0x4080);		/* SPK2/SPK1 routed to OUT8P/N	 OUT8P/N unmuted, Cap interface for Headset */	tsc2101_write(TSC2101_AUDIO_CTRL_6, 0xC048);		/* Headset/Hook switch detect disabled */	 tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);		  volume = ((DEFAULT_VOLUME * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;  	  tsc2101_write(TSC2101_DAC_GAIN_CTRL,(volume<<8)|volume);		DPRINTK("Done codec Init");	/***********End Init_codec *************/        /* clock configuration *///        omap1610_tsc2101_set_samplerate(audio_samplerate);	return tsc; err:	return ERR_PTR(ret);}int tsc2101_open(struct Tsc2101 *tsc){  /*calls tsc2101_program() to configure the registers.*/	tsc->active = 1;	tsc2101_program(tsc);	DPRINTK("open device");	return 0;}void tsc2101_close(struct Tsc2101 *tsc){/*calls tsc2101_wirte to switch off the codec.*/	tsc2101_write(TSC2101_CODEC_POWER_CTRL,0xFFFD);	DPRINTK("close device");}void tsc2101_suspend(struct Tsc2101 *tsc){/* calls tsc2101_wirte to switch off the codec.*/	tsc2101_write(TSC2101_CODEC_POWER_CTRL,0xFFFD);}void tsc2101_resume(struct Tsc2101 *tsc){/* calls tsc2101_program() to configure the registers to open the device */	if(tsc->active)	tsc2101_program(tsc); }EXPORT_SYMBOL(tsc2101_configure);EXPORT_SYMBOL(tsc2101_create);EXPORT_SYMBOL(tsc2101_open);EXPORT_SYMBOL(tsc2101_close);EXPORT_SYMBOL(tsc2101_suspend);EXPORT_SYMBOL(tsc2101_resume);EXPORT_SYMBOL(tsc2101_read);EXPORT_SYMBOL(tsc2101_write);

⌨️ 快捷键说明

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