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 + -
显示快捷键?