📄 ibmcsiti.c
字号:
static struct i2c_client_address_data ibmcsi_i2c_address_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = dummy_i2c_addrlist,
.probe = dummy_i2c_addrlist,
.probe_range = dummy_i2c_addrlist,
.ignore = dummy_i2c_addrlist,
.ignore_range = dummy_i2c_addrlist,
.force = dummy_i2c_addrlist,
};
static int ibmcsi_i2c_id; /* = 0 */
static int ibmcsi_i2c_attach_adapter(struct i2c_adapter *adap)
{
return i2c_probe(adap, &ibmcsi_i2c_address_data,
&ibmcsi_i2c_detect_client);
}
static int ibmcsi_i2c_detach_client(struct i2c_client *client)
{
printk(KERN_WARNING "ibmcsiti: ibmcsi_i2c_detach_client() called. "
"Don't know how to cope :-(\n");
return 0;
}
static void ibmcsi_i2c_inc_use(struct i2c_client *client)
{
MOD_INC_USE_COUNT;
}
static void ibmcsi_i2c_dec_use(struct i2c_client *client)
{
MOD_DEC_USE_COUNT;
}
static int ibmcsi_i2c_detect_client(struct i2c_adapter *adap, int addr,
unsigned short flags, int kind)
{
int err = 0;
struct i2c_client *client;
struct ibmcsiti_state *data;
printk("ibmcsi_i2c_detect_client(): addr=%x\n", addr);
if (! i2c_check_functionality(adap, I2C_FUNC_I2C)) {
printk(KERN_ERR "ibmcsiti: I2C bus lacks functionality\n");
return -ENODEV;
}
/* FIXME: Check the I2C adapter is the one we expect? */
/* This device is write-only, and hence undetectable. We just
* assume we've been called with the right address... (did I
* mention the i2c infrastructure was crap?) */
client = kmalloc(sizeof(struct i2c_client)
+ sizeof(struct ibmcsiti_state), GFP_KERNEL);
if (! client)
return -ENOMEM;
/* This is tricky, but it will set the data to the right value. */
client->data = client + 1;
data = (struct ibmcsiti_state *)client->data;
client->addr = addr;
client->adapter = adap;
client->driver = &ibmcsiti_i2c_driver;
client->flags = 0;
strcpy(client->name, "Arctic-2 codec");
client->id = ibmcsi_i2c_id++; /* FIXME: Racy? */
/* Tell the i2c layer a new client has arrived */
err = i2c_attach_client(client);
if (err) {
printk(KERN_ERR "ibmcsiti: Could not attach I2C client\n");
goto fail;
}
/* Now set up the device */
err = ibmcsiti_i2c_init(client);
if (err) {
i2c_detach_client(client); /* FIXME: ugly, do better */
goto fail;
}
return 0;
fail:
if (client)
kfree(client);
return err;
}
/*****************************************************************************/
/* Part I : DSP device /dev/dsp */
/*****************************************************************************/
static int ibmcsiti_dsp_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
struct ibmcsiti_state *s;
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
s = list_entry(list, struct ibmcsiti_state, devs);
if (!((s->dev_dsp ^ minor) & ~0xf))
break;
}
VALIDATE_STATE(s);
file->private_data = s;
/* wait for device to become free */
down(&s->open_sem);
while (s->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
up(&s->open_sem);
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
up(&s->open_sem);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
down(&s->open_sem);
}
s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE));
spin_lock_irqsave(&s->lock, flags);
/* FIXME: sampling rate hardcoded */
{
struct dma_sgdt *dacp, *adcp;
#define SGDT_COUNT ((PAGE_SIZE/2)/(sizeof(struct dma_sgdt)))
if (file->f_mode & FMODE_WRITE) {
s->dac_free_sgdt_q = (struct dma_sgdt *)(s->write_line);
init_sgdt_q(s->dac_free_sgdt_q, SGDT_COUNT);
/* Prepare default (anchor) descriptor for DAC */
dacp = s->dac_active_sgdt_q = get_sgdt(&(s->dac_free_sgdt_q));
dacp->ccw = (unsigned int)IBMCSI_TXDMA_GO_NOI;
dacp->nextP = virt_to_phys(dacp); /* Loop in on itself */
dacp->srcP = virt_to_phys(&(dacp->dummy)); /* Point to dummy data */
dacp->destP = CSI0_TBUF;
dacp->dummy = 0; /* Dummy neutral data (FIXME: currently assumes signed 16 format) */
dacp->ctrl = 0x80000001; /* Link, count = 1 */
s->dac_sgdt_lastV = dacp;
printk("ibmcsi: s = %p dac free_sgdt_q = %p at %p lastV = %p at %p\n",
s, s->dac_free_sgdt_q, &s->dac_free_sgdt_q, s->dac_sgdt_lastV,
&s->dac_sgdt_lastV);
}
if (file->f_mode & FMODE_READ) {
s->adc_free_sgdt_q = (struct dma_sgdt *)(s->write_line) + SGDT_COUNT;
init_sgdt_q(s->adc_free_sgdt_q, SGDT_COUNT);
#if 0
/* Prepare default (anchor) descriptor for ADC */
adcp = s->adc_active_sgdt_q = get_sgdt(&(s->adc_free_sgdt_q));
adcp->ccw = (unsigned int)IBMCSI_RXDMA_GO_NOI;
adcp->nextP = virt_to_phys(adcp);
adcp->ctrl = 0x80000001;
adcp->srcP = CSI0_RBUF;
adcp->destP = virt_to_phys(&(adcp->dummy));
s->adc_sgdt_lastV = adcp;
#endif
printk("ibmcsi: s = %p adc free_sgdt_q = %p at %p lastV = %p at %p\n",
s, s->adc_free_sgdt_q, &s->adc_free_sgdt_q, s->adc_sgdt_lastV,
&s->adc_sgdt_lastV);
}
}
if (file->f_mode & FMODE_READ) {
s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
s->dma_adc.enabled = 1;
/* FIXME: data format hardcoded */
if ( !(IBMCSI_READ(CSI0_ER) & 1) ) { /* CSI currently not in use */
IBMCSI_WRITE(CSI0_ER, 0); /* Clear enable reg, disable CSI */
IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD); /* Clear CSI errors */
mtdcr(IBMCSI_RXDMA_CR, 0); /* Disable DMA Rx */
mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA) |
DCRN_DMA_SR_ALL(IBMCSI_TXDMA) ) ;
/* Clear DMA status bits */
} /* If currently active, assume it's working OK */
init_timer(&s->adc_timer);
s->adc_timer.function = ibmcsi_adc_timer;
s->adc_timer.data = (unsigned long)s;
s->adc_timer.expires = jiffies + ADC_TIMER_PERIOD;
add_timer(&s->adc_timer);
}
if (file->f_mode & FMODE_WRITE) {
s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags
= s->dma_dac.subdivision = 0;
s->dma_dac.enabled = 1;
/* FIXME: data format hardcoded */
s->dma_dac.hwptr = 0;
s->dma_dac.swptr = 0;
s->dma_dac.count = 0;
s->dma_dac.sg_count = 0;
if ( !(IBMCSI_READ(CSI0_ER) & 1) ) { /* CSI currently not in use */
IBMCSI_WRITE(CSI0_ER, 0); /* Clear enable reg, disable CSI */
IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD); /* Clear CSI errors */
mtdcr(IBMCSI_TXDMA_CR, 0); /* Disable DMA Tx */
mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA) |
DCRN_DMA_SR_ALL(IBMCSI_TXDMA) ) ;
/* Clear DMA status bits */
}
init_timer(&s->dac_timer);
s->dac_timer.function = ibmcsi_dac_timer;
s->dac_timer.data = (unsigned long)s;
s->dac_timer.expires = jiffies + DAC_TIMER_PERIOD;
add_timer(&s->dac_timer);
DBG(printk("ibmcsi: jiffies %d HZ %d timer %d\n", jiffies, HZ,
s->dac_timer.expires));
}
spin_unlock_irqrestore(&s->lock, flags);
up(&s->open_sem);
init_MUTEX(&s->dsp_sem);
return 0;
}
static ssize_t ibmcsiti_dsp_read(struct file *file, char *buffer,
size_t count, loff_t *ppos)
{
struct ibmcsiti_state *s = (struct ibmcsiti_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
down(&s->dsp_sem);
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
goto out;
add_wait_queue(&s->dma_adc.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
swptr = s->dma_adc.swptr;
/* Record curent sgdt for this snapshot */
#if 0
if (s->dma_adc.hwptr >= s->dma_adc.swptr)
cnt = s->dma_adc.hwptr - swptr;
else /* HWPTR wrapped */
cnt = s->dma_adc.dmasize - swptr;
if (s->dma_adc.count < cnt)
cnt = s->dma_adc.count;
if (cnt <= 0)
set_current_state(TASK_INTERRUPTIBLE);
if (cnt > count*2) /* cnt is raw (4 bytes per sample),
* count is cooked (2 bytes per
* sample) */
cnt = count * 2;
#else /* Change to handle multiple interrupts before a successful read */
cnt = s->dma_adc.count; /* Use the count accumulated
* by interrupt handler */
/* cnt is raw (4 bytes per sample), count is cooked (2
* bytes per sample) */
if (cnt > count*2)
cnt = count * 2;
if (cnt + swptr > s->dma_adc.dmasize)
cnt = s->dma_adc.dmasize - swptr;
if (cnt > 0) {
s->dma_adc.swptr = (swptr + cnt) % s->dma_adc.dmasize;
DBG(printk("%8.8x %8.8x\n",s->dma_adc.swptr, s->dma_adc.hwptr));
if (s->dma_adc.swptr <= s->dma_adc.hwptr)
s->adc_sw_prev_sgdt = s->adc_hw_prev_sgdt;
s->dma_adc.count -= cnt;
} else {
set_current_state(TASK_INTERRUPTIBLE);
}
#endif
spin_unlock_irqrestore(&s->lock, flags);
if (cnt <= 0) { /* No data yet */
if (s->dma_adc.enabled)
start_adc(s);
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
goto out;
}
up(&s->dsp_sem);
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out;
}
down(&s->dsp_sem);
if (s->dma_adc.mapped)
{
ret = -ENXIO;
goto out;
}
continue;
}
if (copy_samples_to_user(buffer, s->dma_adc.rawbuf + swptr,
cnt/4, 0)) {
ret = -EFAULT;
goto out;
}
#if 0 /* Change to handle multiple interrupts before read */
swptr = (swptr + cnt) % s->dma_adc.dmasize;
spin_lock_irqsave(&s->lock, flags);
s->dma_adc.swptr = swptr;
s->dma_adc.count -= cnt;
spin_unlock_irqrestore(&s->lock, flags);
#endif
count -= cnt/2;
buffer += cnt/2;
ret += cnt/2;
if (s->dma_adc.enabled)
start_adc(s);
}
out:
up(&s->dsp_sem);
remove_wait_queue(&s->dma_adc.wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static ssize_t ibmcsiti_dsp_write(struct file *file, const char *buffer,
size_t inbytes, loff_t *ppos)
{
struct ibmcsiti_state *s = file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
size_t bytespersample, insamples, outsamples;
long bufspace;
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (! access_ok(VERIFY_READ, buffer, inbytes))
return -EFAULT;
down(&s->dsp_sem);
bytespersample = s->outstereo ? 4 : 2;
insamples = inbytes / bytespersample;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -