📄 ibmcsiti.c
字号:
/*****************************************************************************/
/*
* ibmcsiti.c : IBM PPC 405LP Codec Serial Interface (CSI) +
* Texas Instruments TLV320AIC23 stereo audio codec
* for the Arctic-II reference board
*
* Based on the ibmcsiti driver for IBM PPC 405LP CSI + TLV320AIC23 codec
*
* Copyright (C) 2002 Ken Inoue and David Gibson, IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Supported devices:
* /dev/dsp standard /dev/dsp device, hopefully OSS compatible
* /dev/mixer standard /dev/mixer device, hopefully OSS compatible
*
* TODOs:
* - Integration testing with ViaVoice embedded edition
* - Sampling rate is fixed at 44.1 KHz.
* - Sample format is limited to 16 bit big endian.
* (this is a deviation since an OSS DSP device is supposed to support 8 bit as default.)
* - Drain DAC/ADC
* - Fragment handling
* - MMAP support
* - Split CSI and codec drivers
* - Module parameters
* - Tune retry counts and jiffies
* - Revisit inline functions
* - Write ibmcsi.txt in the Documentation directory
* - Anything else?
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/smp_lock.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/delay.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <asm/time.h>
#include <asm/ocp.h>
#include <platforms/ibm405lp.h>
#include "tlv320aic23.h"
/*****************************************************************************/
/* Start #defines that might belong in ibm405lp.h */
#define CSI0_IO_BASE 0xEF600900 /* CSI I/O base address */
#define CSI0_ER (CSI0_IO_BASE + 0) /* Enable register */
#define CSI0_CFG (CSI0_IO_BASE + 4) /* Configuration register */
#define CSI0_SR (CSI0_IO_BASE + 8) /* Status register */
#define CSI0_TBUF (CSI0_IO_BASE + 0x0C) /* Transmit buffer */
#define CSI0_RBUF (CSI0_IO_BASE + 0x10) /* Receive buffer */
#define CSI0_ID (CSI0_IO_BASE + 0x14) /* ID */
#define CSI0_SCR (CSI0_IO_BASE + 0x18) /* Sleep control register */
#define CSI0_EXTENT 28 /* I/O address extent */
#define CSI_ID_405LP 0x1107 /* CSI core ID (halfword) */
#define CSI_ER_ECSI 0x00000001 /* Enable the CSI */
#define CSI_ER_RXEN 0x00000002 /* Receive enable */
#define CSI_ER_TXEN 0x00000004 /* Transmit enable */
#define CSI_ER_ESLOT(n) (0x80000000 >> (n)) /* Enable (n)th slot */
#define CSI_SR_TOD 0x00000001 /* Transmit overrun */
#define CSI_SR_ROD 0x00000002 /* Receive overrun */
#define CSI_SR_CSIB 0x00008000 /* CSI Busy */
/************************************************************************/
/* DMA Channel Control register */
/* (DCRN_DMA0_CR0 through DCRN_DMA0_CR3) */
/************************************************************************/
#define DCRN_DMA_CR_CE 0x80000000 /* Channel Enable */
#define DCRN_DMA_CR_CIE 0x40000000 /* Channel Interrupt Enable */
#define DCRN_DMA_CR_TD 0x20000000 /* Transfer Direction */
#define DCRN_DMA_CR_PL 0x10000000 /* Peripheral Location */
#define DCRN_DMA_CR_PW_8 0x00000000 /* 8 bit peripheral width */
#define DCRN_DMA_CR_PW_16 0x04000000 /* 16 bit peripheral width */
#define DCRN_DMA_CR_PW_32 0x08000000 /* 32 bit peripheral width */
#define DCRN_DMA_CR_PW_64 0x0c000000 /* 64 bit peripheral width */
#define DCRN_DMA_CR_DAI 0x02000000 /* Destination Addr Increment */
#define DCRN_DMA_CR_SAI 0x01000000 /* Source Address Increment */
#define DCRN_DMA_CR_BE 0x00800000 /* Buffer Enable */
#define DCRN_DMA_CR_TM_BUFFERED 0x00000000 /* Buffered Transfer mode */
#define DCRN_DMA_CR_TM_SW_MEM_TO_MEM 0x00400000 /* Software started mem to mem */
#define DCRN_DMA_CR_TM_HW_MEM_TO_MEM 0x00600000 /* Hardware paced mem to mem */
#define DCRN_DMA_CR_PSC_0 0x00000000 /* 0 Peripheral Setup Cycles */
#define DCRN_DMA_CR_PSC_1 0x00080000 /* 1 Peripheral Setup Cycles */
#define DCRN_DMA_CR_PSC_2 0x00100000 /* 2 Peripheral Setup Cycles */
#define DCRN_DMA_CR_PSC_3 0x00180000 /* 3 Peripheral Setup Cycles */
#define DCRN_DMA_CR_PSC(n) (((n)&0x3)<<19) /* n Peripheral setup cycles */
#define DCRN_DMA_CR_PWC(n) (((n)&0x3f)<<13) /* n peripheral wait cycles */
#define DCRN_DMA_CR_PHC(n) (((n)&0x7)<<10) /* n peripheral hold cycles */
#define DCRN_DMA_CR_ETD 0x00000200 /* EOT/TC Pin Direction */
#define DCRN_DMA_CR_TCE 0x00000100 /* Terminal Count Enable */
#define DCRN_DMA_CR_CP_MASK 0x000000C0 /* Channel Priority */
#define DCRN_DMA_CR_CP(n) (((n)&0x3)<<6)
#define DCRN_DMA_CR_PF 0x00000030 /* Memory read prefetch trans */
#define DCRN_DMA_CR_PF_1 0x00000000 /* Prefetch 1 dword */
#define DCRN_DMA_CR_PF_2 0x00000010 /* Prefetch 2 dword */
#define DCRN_DMA_CR_PF_4 0x00000020 /* Prefetch 4 dword */
#define DCRN_DMA_CR_PCE 0x00000008 /* Parity check enable */
#define DCRN_DMA_CR_DEC 0x00000004 /* Address decrement */
/************************************************************************/
/* DMA Status Register */
/* (Device Control Register bus register DCRN_DMASR) */
/************************************************************************/
/* (n) = DMA channel number, 0-3 */
#define DCRN_DMA_SR_CS(n) (0x80000000 >>(n)) /* Terminal count status */
#define DCRN_DMA_SR_TS(n) (0x08000000 >>(n)) /* End Of Transfer status */
#define DCRN_DMA_SR_RI(n) (0x00800000 >>(n)) /* Error status */
#define DCRN_DMA_SR_IR(n) (0x00080000 >>(n)) /* Internal DMA request pending */
#define DCRN_DMA_SR_ER(n) (0x00008000 >>(n)) /* External DMA request pending */
#define DCRN_DMA_SR_CB(n) (0x00000800 >>(n)) /* Channel Busy */
#define DCRN_DMA_SR_SG(n) (0x00000080 >>(n)) /* Scatter/gather status */
/* Status register bits for the (n)th channel (write to clear) */
#define DCRN_DMA_SR_ALL(n) (DCRN_DMA_SR_CS(n) | \
DCRN_DMA_SR_TS(n) | \
DCRN_DMA_SR_RI(n) | \
DCRN_DMA_SR_IR(n) | \
DCRN_DMA_SR_ER(n) | \
DCRN_DMA_SR_CB(n) | \
DCRN_DMA_SR_SG(n) )
/* DCRN_DMA0_SGC Scatter/Gather Command Register bits */
#define DCRN_DMA_SGC_SSG0 0x80000000
#define DCRN_DMA_SGC_SSG1 0x40000000
#define DCRN_DMA_SGC_SSG2 0x20000000
#define DCRN_DMA_SGC_SSG3 0x10000000
#define DCRN_DMA_SGC_EM0 0x00008000
#define DCRN_DMA_SGC_EM1 0x00004000
#define DCRN_DMA_SGC_EM2 0x00002000
#define DCRN_DMA_SGC_EM3 0x00001000
struct dma_sgdt { /* Must be word aligned */
u32 ccw; /* Channel Control Word */
u32 srcP; /* Source address (physical) */
u32 destP; /* Destination address (physical) */
u32 ctrl; /* MSB = link bit, lower halfword = count */
/* Other 3 bits unused */
u32 nextP; /* Next scatter/gather descriptor list physical address */
/* ------------------------------------- Private use ---------------*/
struct dma_sgdt *prevV; /* Prev scatter/gather descriptor list virtual address */
struct dma_sgdt *nextV; /* Next */
unsigned int dummy; /* Reserved (for 16 byte alignment) */
};
/* End ibm405lp.h candidates */
/*****************************************************************************/
/* Driver specific defines */
/*****************************************************************************/
/* The DMA channels for the CSI are hardcoded in the 405LP chip, so we
* hardcode them. If a future chip adopts programmable channel
* assignment, I expect access to DMA channels would be handled by a
* separate driver.
*/
#define IBMCSI_TXDMA 0 /* Transmit from CSI to codec : channel 0 */
#define IBMCSI_RXDMA 1 /* Receive from codec to CSI : channel 1 */
#define IBMCSI_TXDMA_IRQ 5
#define IBMCSI_RXDMA_IRQ 6
#define IBMCSI_DMA_SR DCRN_DMASR
/* Transmit (playback) DMA registers */
#define IBMCSI_TXDMA_CR DCRN_DMACR0
#define IBMCSI_TXDMA_DA DCRN_DMADA0
#define IBMCSI_TXDMA_SA DCRN_DMASA0
#define IBMCSI_TXDMA_CT DCRN_DMACT0
/* Receive (capture) DMA registers */
#define IBMCSI_RXDMA_CR DCRN_DMACR1
#define IBMCSI_RXDMA_DA DCRN_DMADA1
#define IBMCSI_RXDMA_SA DCRN_DMASA1
#define IBMCSI_RXDMA_CT DCRN_DMACT1
#define IBMCSI_TXDMA_CONFIG ( /* Channel disabled */ \
DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \
/* Memory to peripheral */ \
DCRN_DMA_CR_PL | /* Peripheral on OPB */ \
DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \
/* Dest address not incremented */ \
DCRN_DMA_CR_SAI | /* Source address incremented */ \
/* Peripheral transfer mode */ \
/* Peripheral setup cycle 0 */ \
DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \
/* Peripheral hold cycle 0 */ \
DCRN_DMA_CR_ETD | /* EOTn = TC */ \
DCRN_DMA_CR_TCE ) /* Terminal count enable */
#define IBMCSI_TXDMA_GO (IBMCSI_TXDMA_CONFIG | DCRN_DMA_CR_CE) /* For int */
#define IBMCSI_TXDMA_GO_NOI (IBMCSI_TXDMA_GO & ~DCRN_DMA_CR_CIE) /* For polling */
#define IBMCSI_RXDMA_CONFIG ( /* Channel disabled */ \
DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \
DCRN_DMA_CR_TD | /* Peripheral to memory */ \
DCRN_DMA_CR_PL | /* Peripheral on OPB */ \
DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \
DCRN_DMA_CR_DAI | /* Dest address incremented */ \
/* Source address not incremented */ \
/* Peripheral transfer mode */ \
/* Peripheral setup cycle 0 */ \
DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \
/* Peripheral hold cycle 0 */ \
DCRN_DMA_CR_ETD | /* EOTn = TC */ \
DCRN_DMA_CR_TCE ) /* Terminal count enable */
#define IBMCSI_RXDMA_GO (IBMCSI_RXDMA_CONFIG | DCRN_DMA_CR_CE)
#define IBMCSI_RXDMA_GO_NOI (IBMCSI_RXDMA_GO & ~DCRN_DMA_CR_CIE)
#define IBMCSI_DEFAULT_SAMPLING_RATE 44100
#define IBMCSI_TI_CFG 0x00400010 /* 64 bits per frame mode */
#define DAC_TIMER_PERIOD (HZ/50)
#define ADC_TIMER_PERIOD (HZ/50)
#define TX_SG DCRN_ASG0
#define RX_SG DCRN_ASG1
#define TX_SG_ENABLE DCRN_DMA_SGC_SSG0
#define TX_SG_MASK DCRN_DMA_SGC_EM0
#define RX_SG_ENABLE DCRN_DMA_SGC_SSG1
#define RX_SG_MASK DCRN_DMA_SGC_EM1
/*****************************************************************************/
#undef OSS_DOCUMENTED_MIXER_SEMANTICS /* FIXME: does this have any effect? */
#define DBG(x) {}
/*#define DBG(x) {x}*/
#define IBMCSI_MAGIC 0xB31BCB /* Copied from the Austin Research version */
/* TODO: verify this value is legitimate */
#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) /* FIXME: check out this value */
#define DMABUF_MINORDER 1 /* FIXME: ditto */
#define IBMCSI_WRITE(reg, val) (__raw_writel(val, reg))
#define IBMCSI_READ(reg) (__raw_readl(reg))
#define VALIDATE_STATE(s) \
({ \
if (!(s) || (s)->magic != IBMCSI_MAGIC) { \
printk(KERN_ERR "ibmcsi: invalid signature (magic) in private data\n");\
return -ENXIO; \
} \
})
/*****************************************************************************/
/* Static variables, globals and structs */
/*****************************************************************************/
static const unsigned sample_shift[] = {0, 1, 1, 2};
static LIST_HEAD(devs);
/*
* Private data structure for the devices supported by this driver
*/
struct ibmcsiti_state {
unsigned int magic; /* Magic signature value for sanity check */
unsigned int state; /* Driver state (DAC/ADC running, Halt, etc.) */
struct i2c_client *i2c;
/* For multi-device support; not used for the 405LP */
struct list_head devs;
/* DSP device variables */
int dev_dsp; /* unit number of our registered DSP device */
int outstereo; /* are we in stereo output mode? */
spinlock_t lock;
struct semaphore dsp_sem;
struct semaphore open_sem;
mode_t open_mode;
wait_queue_head_t open_wait;
/* Mixer device variables */
int dev_mixer; /* unit number of our registered mixer device */
struct semaphore mix_sem;
/* Buffers */
unsigned char *write_line;
dma_addr_t dma_write_line;
unsigned char *read_line;
dma_addr_t dma_read_line;
/* Control blocks for audio playback (dac) and capture (adc) */
struct dmabuf {
/* The important ones... */
void *rawbuf; /* DMA buffer logical address */
dma_addr_t dmaaddr; /* DMA buffer physical address */
unsigned hwptr, swptr; /* Offsets from rawbuf for data. HWPTR = DMAC, SWPTR = driver */
int count;
wait_queue_head_t wait;
/* And the rest, inherited from sample drivers... */
unsigned fragsize;
unsigned dmasize;
unsigned fragsamples;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
unsigned endcleared:1;
unsigned enabled:1;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
unsigned total_bytes;
unsigned error; /* Over/underrun */
unsigned sg_count;
} dma_dac, dma_adc;
/* FIXME: should move the following to dma_dac/dma_adc (trivial) */
struct timer_list dac_timer;
struct timer_list adc_timer;
struct dma_sgdt *dac_free_sgdt_q;
struct dma_sgdt *adc_free_sgdt_q;
struct dma_sgdt *dac_active_sgdt_q;
struct dma_sgdt *adc_active_sgdt_q;
struct dma_sgdt *dac_sgdt_lastV; /* Anchors */
struct dma_sgdt *adc_sgdt_lastV;
struct dma_sgdt *adc_hw_prev_sgdt;
struct dma_sgdt *adc_sw_prev_sgdt;
/* Local copy of TI codec settings (the registers being write only) */
u16 codec_reg[TLV320_REG_EXTENT]; /* the registers are
* actually 9-bits each */
};
/* Driver state flags */
#define IBMCSI_DAC_RUNNING 0x00010000
#define IBMCSI_ADC_RUNNING 0x00020000
#define IBMCSI_HALT 0x00040000
/************************************************************************/
/* Misc function prototypes */
/************************************************************************/
static int __init init_ibmcsiti(void); /* Driver initialization */
static void __exit cleanup_ibmcsiti(void); /* Driver exit cleanup */
static int ibmcsi_i2c_attach_adapter(struct i2c_adapter *);
static int ibmcsi_i2c_detach_client(struct i2c_client *);
static int ibmcsi_i2c_detect_client(struct i2c_adapter *, int address,
unsigned short flags, int kind);
static void ibmcsi_i2c_inc_use(struct i2c_client *);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -