📄 ibmcsiti.c
字号:
static void ibmcsi_i2c_dec_use(struct i2c_client *);
/************************************************************************/
/* DSP driver function prototypes */
/************************************************************************/
/* Top level */
static int ibmcsiti_dsp_open(struct inode *inode, struct file *file);
static ssize_t ibmcsiti_dsp_read(struct file *file, char *buffer,
size_t count, loff_t *ppos);
static ssize_t ibmcsiti_dsp_write(struct file *file, const char *buffer,
size_t count, loff_t *ppos);
static unsigned int ibmcsiti_dsp_poll(struct file *file,
struct poll_table_struct *wait);
static int ibmcsiti_dsp_release(struct inode *inode, struct file *file);
static int ibmcsiti_dsp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
/* Interrupt handlers */
static void ibmcsiti_dac_interrupt(int irq, void *dev_id,
struct pt_regs *regs);
static void ibmcsiti_adc_interrupt(int irq, void *dev_id,
struct pt_regs *regs);
static void ibmcsi_adc_timer(unsigned long param);
static void ibmcsi_dac_timer(unsigned long param);
/* Utility routines */
static unsigned long copy_samples_to_user(char *dest, const char *src,
unsigned nsamples, int stereo);
static unsigned long copy_samples_from_user(char *dest, const char *src,
unsigned nsamples, int stereo);
static void start_adc(struct ibmcsiti_state *s);
static void start_dac(struct ibmcsiti_state *s);
static int drain_dac(struct ibmcsiti_state *s, int nonblock);
static inline void stop_adc(struct ibmcsiti_state *s);
static inline void stop_dac(struct ibmcsiti_state *s);
static int ibmcsi_stop_csi_sync(void);
static inline void dealloc_dmabuf(struct ibmcsiti_state *s,
struct dmabuf *buf);
static int prog_dmabuf(struct ibmcsiti_state *s, struct dmabuf *buf,
unsigned rate, unsigned fmt,unsigned adc_init);
static inline int prog_dmabuf_adc(struct ibmcsiti_state *s);
static inline int prog_dmabuf_dac(struct ibmcsiti_state *s);
static inline unsigned get_hwptr(struct ibmcsiti_state *s, struct dmabuf *buf,
unsigned channel);
static void ibmcsiti_update_ptr(struct ibmcsiti_state *s);
static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr,
unsigned len, unsigned char c);
static inline unsigned ld2(unsigned int x);
/* Scatter/Gather descriptor table maintenance routines */
static void init_sgdt_q(struct dma_sgdt *queue, int count);
static struct dma_sgdt *get_sgdt(struct dma_sgdt **queueaddress);
static void free_sgdt(struct dma_sgdt **queueaddress, struct dma_sgdt *dt);
static unsigned int check_sgdt_range(struct ibmcsiti_state *s,
struct dma_sgdt* dt, int count);
/************************************************************************/
/* Mixer driver function prototypes */
/************************************************************************/
/* Top level */
static int ibmcsiti_mixer_open(struct inode *inode, struct file *file);
static int ibmcsiti_mixer_ioctl(struct inode *ioctl, struct file *file,
unsigned int cmd, unsigned long arg);
static int ibmcsiti_mixer_release(struct inode *inode, struct file *file);
static int mixer_read_ioctl(struct ibmcsiti_state *s, unsigned int nr,
caddr_t arg);
static int mixer_write_ioctl(struct ibmcsiti_state *s, unsigned int nr,
caddr_t arg);
/* Mixer read routines */
static int ibmcsiti_get_volume(struct ibmcsiti_state *s) ;
static int ibmcsiti_get_line(struct ibmcsiti_state *s) ;
static int ibmcsiti_get_mic(struct ibmcsiti_state *s) ;
static int ibmcsiti_get_recsrc(struct ibmcsiti_state *s) ;
static int ibmcsiti_get_outsrc(struct ibmcsiti_state *s) ;
/* Mixer write routines */
static int ibmcsiti_set_volume(struct ibmcsiti_state *s, int val);
static int ibmcsiti_set_line(struct ibmcsiti_state *s, int val) ;
static int ibmcsiti_set_mic(struct ibmcsiti_state *s, int val) ;
static int ibmcsiti_set_recsrc(struct ibmcsiti_state *s, int val) ;
/************************************************************************/
/* Helper functions */
/************************************************************************/
/* TLV320AIC23 control register access routines */
static u16 tlv320_read_reg(struct ibmcsiti_state *s, int reg);
static void tlv320_write_reg(struct ibmcsiti_state *s, int reg,
u16 val);
/************************************************************************/
MODULE_AUTHOR("David Gibson");
MODULE_DESCRIPTION("IBM PPC 405LP CSI / TI TLV320AIC23 Audio Driver");
/*****************************************************************************/
/* Initialization tables */
/*****************************************************************************/
static /*const*/ struct file_operations ibmcsiti_audio_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ibmcsiti_dsp_read,
.write = ibmcsiti_dsp_write,
.poll = ibmcsiti_dsp_poll,
.ioctl = ibmcsiti_dsp_ioctl,
.mmap = NULL,
.open = ibmcsiti_dsp_open,
.release = ibmcsiti_dsp_release,
};
static /*const*/ struct file_operations ibmcsiti_mixer_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = ibmcsiti_mixer_ioctl,
.open = ibmcsiti_mixer_open,
.release = ibmcsiti_mixer_release,
};
static struct i2c_driver ibmcsiti_i2c_driver = {
.name = "IBMCSI+TLV320AIC23 codec",
.id = 0xf314,
.flags = I2C_DF_NOTIFY,
.attach_adapter = ibmcsi_i2c_attach_adapter,
.detach_client = ibmcsi_i2c_detach_client,
.command = NULL,
.inc_use = ibmcsi_i2c_inc_use,
.dec_use = ibmcsi_i2c_dec_use,
};
typedef int (*PIF) (struct ibmcsiti_state *, int);
static struct inittable {
PIF proc;
int val;
} inittable[] __initdata = {
{ibmcsiti_set_recsrc, SOUND_MASK_MIC},
{ibmcsiti_set_volume, 100 * 35/47},
{ibmcsiti_set_line, 100},
{ibmcsiti_set_mic, 100},
};
/*****************************************************************************/
/* Driver initialization and cleanup */
/*****************************************************************************/
static int __init init_ibmcsiti(void)
{
int ret;
/* We just register the I2C driver here. The initialization
* of the device proper happens when the I2C layer calls us
* back. */
printk(KERN_INFO "ibmcsiti: compiled at " __TIME__ " " __DATE__ "\n");
ret = i2c_add_driver(&ibmcsiti_i2c_driver);
if (ret)
printk(KERN_ERR "ibmcsiti: i2c_add_driver() failed\n");
return ret;
}
static int ibmcsiti_i2c_init(struct i2c_client *client)
{
struct ibmcsiti_state *s = client->data;
int ret;
int i;
unsigned long end_time;
void *codec_lines_virt;
dma_addr_t codec_lines;
printk("ibmcsiti_i2c_init(): addr=0x%x\n", client->addr);
/* Power up the CSI core during initialization */
ppc4xx_cpm_fr(IBM_CPM_CSI, 0);
/* Allocate line buffers */
codec_lines_virt = consistent_alloc(GFP_DMA, PAGE_SIZE, &codec_lines);
if (! codec_lines_virt) {
printk(KERN_ERR "ibmcsiti: out of memory\n");
/* FIXME we should turn the CSI off again if init fails */
ret = -ENOMEM;
goto err_linebuffer;
}
if (! request_mem_region(CSI0_IO_BASE, CSI0_EXTENT, "ibmcsiti")) {
printk(KERN_ERR "ibmcsiti: I/O ports %x-%x in use\n",
CSI0_IO_BASE, CSI0_EXTENT-1);
return -EBUSY;
}
/* Check CSI core presence */
if ( (IBMCSI_READ(CSI0_ID) >> 16) != CSI_ID_405LP) {
printk(KERN_WARNING "ibmcsiti: Unexpected CSI ID %x\n",
IBMCSI_READ(CSI0_ID));
ret = -ENODEV;
goto err_irq1;
}
memset(s, 0, sizeof(struct ibmcsiti_state));
s->i2c = client;
s->magic = IBMCSI_MAGIC;
init_waitqueue_head(&s->dma_adc.wait);
init_waitqueue_head(&s->dma_dac.wait);
init_waitqueue_head(&s->open_wait);
init_MUTEX(&s->open_sem);
spin_lock_init(&s->lock);
s->write_line = codec_lines_virt;
s->dma_write_line = codec_lines;
s->read_line = (void *)((char *)codec_lines_virt + 128);
s->dma_read_line = codec_lines + 128;
/* Interrupts - currently held indefinitely since no one else
* will be using them. Mixer and DSP use the same
* interrupts. */
ret = request_irq(IBMCSI_TXDMA_IRQ, ibmcsiti_dac_interrupt,
SA_SHIRQ, "ibmcsiti", s);
if (ret != 0) {
printk(KERN_ERR "ibmcsiti: IRQ %d in use\n", IBMCSI_TXDMA_IRQ);
goto err_irq1;
}
ret = request_irq(IBMCSI_RXDMA_IRQ, ibmcsiti_adc_interrupt,
SA_SHIRQ, "ibmcsiti", s);
if (ret != 0) {
printk(KERN_ERR "ibmcsiti: IRQ %d in use\n",IBMCSI_RXDMA_IRQ);
goto err_irq2;
}
#if 0 /* FIXME: CSI error interrupts not implemented */
ret=request_irq(21, ibmcsiti_csi_interrupt, SA_SHIRQ, "ibmcsiti",s);
if (ret) {
printk(KERN_ERR "ibmcsiti: irq 21 in use\n");
goto err_irq;
}
#endif
/* register devices */
ret = s->dev_dsp = register_sound_dsp(&ibmcsiti_audio_fops, -1);
if (ret < 0) {
printk(KERN_ERR "ibmcsiti: Could not register DSP.\n");
goto err_dev1;
}
ret = s->dev_mixer = register_sound_mixer(&ibmcsiti_mixer_fops, -1);
if (ret < 0) {
printk(KERN_ERR "ibmcsiti: Could not register mixer.\n");
goto err_dev2;
}
/* Initialize the cores / chip: DMA controller, CSI and codec.*/
/* Turn on the DMA controller */
#if 0 /* Unnecessary - Bishop */
end_time = jiffies + HZ/10;
mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) & (~CPM_DMA));
while (! (mfdcr(DCRN_CPC0_SR) & CPM_DMA) ) { /* DMAC asleep? */
mfdcr(IBMCSI_DMA_SR); /* Dummy read to wake it up */
mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) & (~CPM_DMA)); /* Turn on DMA Controller */
asm("isync");
if (jiffies > end_time) {
printk(KERN_ERR "ibmcsiti: cannot turn on DMA controller \n");
goto err_dev3;
}
}
/* Caveat : If you don't do the above and the DMA controller
happens to be turned off, the controller will still respond
to DCR reads/writes and even change state to DRQ Pending /
Channel Active when you try to kick off DMA, but will
adamantly refuse to do any real work. This can happen if the
kernel is configured without PPC405 DMA support, or if a
power management driver / daemon tried to save power. We
therefore power up the DMA controller before doing anything
with it and codec. */
#endif
IBMCSI_WRITE(CSI0_ER,0); /* Stop CSI */
/* Set up TI codec via direct I2C manipulation. */
/* The control registers are write-only, so we have to init
* them to a known state. */
{
int i2c_error = 0;
u16 i2c_data[] = {
TLV320_REG_WRITE(TLV320_RR, 0x0000), /* 0x1e00: Reset chip */
TLV320_REG_WRITE(TLV320_DIA, 0x0000), /* 0x1200: Deactivate digital interface */
TLV320_REG_WRITE(TLV320_DAI, 0x0053), /* 0x0e53: Master, MSB on 2nd BCLK, DSP format */
TLV320_REG_WRITE(TLV320_PDC, 0x0100), /* 0x0d00: Power up everything */
TLV320_REG_WRITE(TLV320_DIA, 0x0001), /* 0x1201: Activate digital interface */
TLV320_REG_WRITE(TLV320_LLI, 0x0017), /* 0x0017: Left Line-In volume 0dB */
TLV320_REG_WRITE(TLV320_RLI, 0x0017), /* 0x0217: Right Line-In volume 0dB */
TLV320_REG_WRITE(TLV320_LCH, 0x00F9), /* 0x04f9: Left headphone volume 0dB */
TLV320_REG_WRITE(TLV320_RCH, 0x00F9), /* 0x06f9: Right headphone volume 0dB */
TLV320_REG_WRITE(TLV320_DAP, 0x0004) /* 0x0a04: DAC enable */
};
for (i=0; i < (sizeof(i2c_data) / sizeof(i2c_data[0])); i++) {
if (i2c_master_send(client, (u8 *)(i2c_data+i), 2) != 2) {
printk(KERN_ERR "ibmcsiti: I2C write error, "
"cannot set up audio chip.\n");
i2c_error = 1;
break;
}
udelay(1000);
/* Undocumented, but some commands
* apparently require some delay. */
}
}
/* End I2C code */
/* Initialize shadow registers */
{
u16 codec_reg_init[TLV320_REG_EXTENT] = {0x0117,
0x0117,
0x01f9,
0x01f9,
0x0010,
0x0004,
0x0100,
0x0053,
0x0020,
0x0001,};
for (i=0; i<TLV320_REG_EXTENT; i++)
s->codec_reg[i] = codec_reg_init[i];
}
/* Initialize mixer settings (see inittable[] above for
* procedures and values.*/
for (i=0; i < (sizeof(inittable)/sizeof(inittable[0])); i++)
inittable[i].proc(s, inittable[i].val);
/* Add to driver list */
list_add_tail(&s->devs, &devs);
return 0;
err_dev3: /* Error exit initializing chips */
unregister_sound_mixer(s->dev_mixer);
err_dev2: /* Error exit registering mixer device */
unregister_sound_dsp(s->dev_dsp);
err_dev1:
free_irq(IBMCSI_TXDMA_IRQ, s);
err_irq2:
free_irq(IBMCSI_RXDMA_IRQ, s);
#if 0 /* FIXME */
free_irq(21,s);
#endif
err_irq1:
release_mem_region(CSI0_IO_BASE, CSI0_EXTENT);
err_linebuffer:
consistent_free(codec_lines_virt);
ppc4xx_cpm_fr(IBM_CPM_CSI, 1); /* Turn the core off again */
return ret;
}
static void __exit cleanup_ibmcsiti(void)
{
struct ibmcsiti_state *s;
static void *codec_lines_virt;
while (devs.next != &devs) {
s = list_entry(devs.next, struct ibmcsiti_state, devs);
list_del(devs.next);
unregister_sound_mixer(s->dev_mixer);
unregister_sound_dsp(s->dev_dsp);
free_irq(IBMCSI_TXDMA_IRQ, s);
free_irq(IBMCSI_RXDMA_IRQ, s);
release_mem_region(CSI0_IO_BASE, CSI0_EXTENT);
codec_lines_virt = s->write_line;
kfree(s);
consistent_free(codec_lines_virt);
}
}
module_init(init_ibmcsiti);
module_exit(cleanup_ibmcsiti);
/*****************************************************************************/
/* I2C Callbacks */
/*****************************************************************************/
/* Blech. It's not as bad as PCMCIA, but boy does the i2c
* infrastructure suck... */
static unsigned short normal_i2c[] = {0x1a, I2C_CLIENT_END,};
static unsigned short dummy_i2c_addrlist[] = {I2C_CLIENT_END,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -