* I2S MPC5200
@ 2005-09-16 13:29 Arnaud Carer
2005-09-16 16:16 ` Bob Peterson
0 siblings, 1 reply; 2+ messages in thread
From: Arnaud Carer @ 2005-09-16 13:29 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 640 bytes --]
Hi,
I am looking for an I2s Linux driver to implement an audio output with the
mpc5200
I2s.c and i2s_ring.c include in the DENX ELDK kernel seems to be wrong.
>i2s.c is not really a driver, but a (very OLD) test version of one
>used to demonstrate certain BestComm related problems. Don't try
>using it as a real driver ;-)
>The ring driver appears to be RX only,
Thanks,
Arnaud
___________________________________
Arnaud Carer
Firmware Development
<http://www.cleode.fr/> <http://www.cleode.fr/> CLEODE
<http://www.cleode.fr> www.cleode.fr
Tel: 02 96 48 68 18
Fax: 02 96 48 19 11
[-- Attachment #2: Type: text/html, Size: 6536 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: I2S MPC5200
2005-09-16 13:29 I2S MPC5200 Arnaud Carer
@ 2005-09-16 16:16 ` Bob Peterson
0 siblings, 0 replies; 2+ messages in thread
From: Bob Peterson @ 2005-09-16 16:16 UTC (permalink / raw)
To: Arnaud Carer, linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 1122 bytes --]
Hi Arnaud (and all),
My I2S audio driver seems to work fine for our icecube-based mpc5200.
It is attached for all the world to smear. Hardware assumptions are
included in
the comments. It assumes a pcm1717 i2s dac running on psc2.
It implements much of the oss and mixer ioctls, volume controls, etc.
It seems to be pretty stable:
I've fixed many interrupt and timing problems of the original and
played many songs for many days with this version of the driver.
Regards,
Bob Peterson
bob@acdstar.com
At 08:29 AM 9/16/2005, Arnaud Carer wrote:
>Hi,
>I am looking for an I2s Linux driver to implement an audio output
>with the mpc5200
>
>I2s.c and i2s_ring.c include in the DENX ELDK kernel seems to be wrong.
>
> >i2s.c is not really a driver, but a (very OLD) test version of one
> >used to demonstrate certain BestComm related problems. Don't try
> >using it as a real driver ;-)
> >The ring driver appears to be RX only,
>Thanks,
>Arnaud
>___________________________________
>
>Arnaud Carer
>Firmware Development
>
>CLEODE
><http://www.cleode.fr>www.cleode.fr
>Tel: 02 96 48 68 18
>Fax: 02 96 48 19 11
[-- Attachment #2: i2s.c --]
[-- Type: text/plain, Size: 36555 bytes --]
/* Converted from original i2s.c to work with PCM1717 dac by Bob Peterson, */
/* Advanced Communication Design, bob@acdstar.com 4/7/2005 */
/* Assumptions made for our hardware:
Assumes PSC2 is attached to your i2s audio dac
The control lines are on GPIO pins, mapped as follows:
GPIO "Timer 0" = Das Blinken Light
GPIO "Timer 1" = IDE Power
GPIO "Timer 2" = RSTB on PCM1717
GPIO "Timer 3" = MC on PCM1717
GPIO "Timer 4" = ML on PCM1717
GPIO "Timer 5" = MD on PCM1717
GPIO "Timer 6" = Backlight enable
GPIO "Timer 7" = Beeper
Other assumptions:
This assumes your primary audio device is /dev/I2S
Some programs, such as mplayer, assume the audio device is /dev/dsp or dspw
I just made a symlink, but you can change how the device is registered too.
Changes from the original i2s driver included in eldk:
1. Receive functions and interrupts are disabled (transmit only)
2. Added ioctls for oss sound device
3. Added ioctls for mixer device
4. Added extra ioctl to sound a beeper for a given length of time
5. Changed how buffering and interrupts are handled.
6. Added I2S control functions (such as volume control) separate from DAC.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/delay.h>
#include <linux/init.h>
#include <asm/mpc5xxx.h>
#include <bestcomm_api.h>
#include <mgt5200/mgt5200.h>
#include <mgt5200/sdma.h>
#include <asm/mpc5xxx_i2s.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include </ppc/eldk/ppc_6xx/usr/src/linux/drivers/sound/sound_config.h>
#define I2S_MINOR 242
#define BEEP_DURATION 5
#define MAX_DMA_BUFFERS 16
/* #define ENABLE_RX */
#define RX_TASK_START
#define TX_TASK_START
/* #define RDEBUG */
/* #define WDEBUG */
/* #define LITEDEBUG */
#define DEBUG_LOST_INT
#ifdef RDEBUG
#define RDPRINTK(fmt, args...) printk(fmt, ## args)
#else
#define RDPRINTK(fmt, args...)
#endif
#ifdef WDEBUG
#define WDPRINTK(fmt, args...) printk(fmt, ## args)
#else
#define WDPRINTK(fmt, args...)
#endif
/* Some bit assignments for SICR */
#define DELAY_TIME_SLOT 0x20000000
#define GEN_CLK_INT 0x00800000
#define MULTIWD_ENABLE 0x00400000
#define CLK_POL_RISING 0x00200000
#define LSB_FIRST 0x10000000
#define I2S_WAIT 3 /* 3 microsecond delay between I2S ops */
#define reg1717_vol_l 0
#define reg1717_vol_r 1
#define reg1717_vol_ldl 0x0100
#define reg1717_mute 2
#define reg1717_io_fmt 3
#define reg1717_io_iis 0x0001 /* low=normal, high=iis data fmt */
#define reg1717_io_lrp 0x0002 /* priority of sample rate clock */
#define reg1717_io_iw 0x0004 /* input word length, low=16 h=18 */
#define reg1717_io_atc 0x0008 /* attenuation ctrl. 0=individual
1=left+right controlled by reg0 */
#define reg1717_io_output_mute 0x0000 /* all mute */
#define reg1717_io_output_m_r 0x0010 /* left is mute , right is right */
#define reg1717_io_output_m_l 0x0020 /* left is mute , right is left */
#define reg1717_io_output_m_a 0x0030 /* left is mute , right is l+r/2 */
#define reg1717_io_output_r_m 0x0040 /* left is right, right is mute */
#define reg1717_io_output_r_r 0x0050 /* left is right, right is right */
#define reg1717_io_output_r_l 0x0060 /* left is right, right is left */
#define reg1717_io_output_r_a 0x0070 /* left is right, right is l+r/2 */
#define reg1717_io_output_l_m 0x0080 /* left is left , right is mute */
#define reg1717_io_output_l_r 0x0090 /* left is left , right is right */
#define reg1717_io_output_l_l 0x00A0 /* left is left , right is left */
#define reg1717_io_output_l_a 0x00B0 /* left is left , right is l+r/2 */
#define reg1717_io_output_a_m 0x00C0 /* left is l+r/2, right is mute */
#define reg1717_io_output_a_r 0x00D0 /* left is l+r/2, right is right */
#define reg1717_io_output_a_l 0x00E0 /* left is l+r/2, right is left */
#define reg1717_io_output_a_a 0x00F0 /* left is l+r/2, right is l+r/2 */
#define reg1717_io_normal (reg1717_io_output_l_r | \
reg1717_io_atc /* | \
reg1717_io_iis*/ ) /* 99 */
#ifdef ENABLE_RX
static volatile ulong rx_released; /* Counter of released BDs */
static volatile ulong rx_assigned; /* Counter of BDs to be assigned */
static volatile ulong rx_data; /* Counter of BDs read */
#endif
static volatile ulong tx_released; /* Counter of released BDs */
static volatile ulong tx_assigned; /* Counter of BDs to be assigned */
#ifdef DEBUG_LOST_INT
static volatile int tx_int;
static volatile ulong tx_acount; /* real number of assigned BDs */
#endif
static int txtask;
static int initiator_tx;
#ifdef ENABLE_RX
static int rxtask;
static int initiator_rx;
#endif
static int psc_num;
static unsigned long i2s_is_open;
static int configured=0;
static int once;
static int sample_rate = 44100;
static int mixer_dev_id;
static int blinken = 0;
static int beep_duration = 0;
static struct i2s_ioctl defaults;
static DECLARE_WAIT_QUEUE_HEAD(WaitQ);
static void i2s_start_rx_tx (int task);
static void psc_enable(void);
DECLARE_WAIT_QUEUE_HEAD (i2s_wait_tx);
#ifdef ENABLE_RX
DECLARE_WAIT_QUEUE_HEAD (i2s_wait_rx);
#endif
extern int mgt_sdma_load_tasks_image(void);
struct i2s_buffer {
char *va;
dma_addr_t pa;
size_t len;
};
static void timer_int(void *nothing);
static inline void wait(void) { udelay(I2S_WAIT); }
static int i2s_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg);
static struct tq_struct Task = {
routine: (void(*)(void *)) timer_int, /* The function to run */
data: NULL
};
static struct i2s_buffer i2s_tx_bufs[MAX_DMA_BUFFERS];
#ifdef ENABLE_RX
static struct i2s_buffer i2s_rx_bufs[MAX_DMA_BUFFERS];
#endif
/* --------------- */
/* ML = frame sync */
/* --------------- */
static void ml_low(void) { /* GPIO Timer 4 */
u32 gpt4;
gpt4=*(volatile u32 *)MPC5xxx_GPT4_ENABLE; /* get initial value */
gpt4&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt4|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT4_ENABLE=gpt4;
}
static void ml_high(void){
u32 gpt4;
gpt4=*(volatile u32 *)MPC5xxx_GPT4_ENABLE; /* get initial value */
gpt4&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt4|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT4_ENABLE=gpt4;
}
/* --------------- */
/* MC = clock */
/* --------------- */
static void mc_low(void) { /* GPIO Timer 3 */
u32 gpt3;
gpt3=*(volatile u32 *)MPC5xxx_GPT3_ENABLE; /* get initial value */
gpt3&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt3|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT3_ENABLE=gpt3;
}
static void mc_high(void){
u32 gpt3;
gpt3=*(volatile u32 *)MPC5xxx_GPT3_ENABLE; /* get initial value */
gpt3&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt3|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT3_ENABLE=gpt3;
}
/* --------------- */
/* MD = data */
/* --------------- */
static void md_low(void) { /* GPIO Timer 5 */
u32 gpt5;
gpt5=*(volatile u32 *)MPC5xxx_GPT5_ENABLE; /* get initial value */
gpt5&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt5|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT5_ENABLE=gpt5;
}
static void md_high(void){
u32 gpt5;
gpt5=*(volatile u32 *)MPC5xxx_GPT5_ENABLE; /* get initial value */
gpt5&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt5|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT5_ENABLE=gpt5;
}
/* --------------- */
/* RSTB = Reset */
/* --------------- */
static void snd_reset(void) { /* GPIO Timer 2 */
u32 gpt2;
gpt2=*(volatile u32 *)MPC5xxx_GPT2_ENABLE; /* get initial value f0000620 */
gpt2&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt2|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT2_ENABLE=gpt2;
}
static void snd_normal(void) {
u32 gpt2;
gpt2=*(volatile u32 *)MPC5xxx_GPT2_ENABLE; /* get initial value */
gpt2&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt2|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT2_ENABLE=gpt2;
}
/* --------------- */
/* Blinken Licht */
/* --------------- */
static void blinken_off(void) { /* GPIO Timer 0 */
u32 gpt0;
gpt0=*(volatile u32 *)MPC5xxx_GPT0_ENABLE; /* get initial value */
gpt0&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt0|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT0_ENABLE=gpt0;
}
static void blinken_on(void) {
u32 gpt0;
gpt0=*(volatile u32 *)MPC5xxx_GPT0_ENABLE; /* get initial value */
gpt0&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt0|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT0_ENABLE=gpt0;
}
/* --------------- */
/* Beeper */
/* --------------- */
static void beeper_off(void) { /* GPIO Timer 0 */
u32 gpt7;
gpt7=*(volatile u32 *)MPC5xxx_GPT7_ENABLE; /* get initial value */
gpt7&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt7|=0x00000024; /* Set as a GPIO and set the value to zero */
*(volatile u32 *)MPC5xxx_GPT7_ENABLE=gpt7;
}
static void beeper_on(void) {
u32 gpt7;
gpt7=*(volatile u32 *)MPC5xxx_GPT7_ENABLE; /* get initial value */
gpt7&=0xffffff00; /* clear the lower bits, preserve the upper bits */
gpt7|=0x00000034; /* Set as a GPIO and set the value to one */
*(volatile u32 *)MPC5xxx_GPT7_ENABLE=gpt7;
}
/* ------------------------------------------------------------------------- */
/* i2s_sendbit */
/* ------------------------------------------------------------------------- */
static void i2s_sendbit(int bit, int bit_num)
{
mc_low();
wait();
if (bit)
md_high();
else
md_low();
wait();
wait();
mc_high();
wait();
if (bit_num==0)
ml_low();
wait();
if (bit_num==0) {
wait();
ml_high();
}
}
/* ------------------------------------------------------------------------- */
/* i2s_sendword */
/* ------------------------------------------------------------------------- */
static void i2s_sendword(unsigned short word)
{
static int i,v;
ml_high();
for (i=15; i>=0; i--) {
v=(word>>i) & 0x00000001;
i2s_sendbit(v,i);
}
wait();
wait();
wait();
wait();
}
/* ------------------------------------------------------------------------- */
/* tipcm1717setregister */
/* ------------------------------------------------------------------------- */
static void tipcm1717setregister(unsigned short reg,unsigned short val)
{
static short regval;
regval=(val | (reg<<9));
i2s_sendword(regval);
}
/* ------------------------------------------------------------------------- */
/* mixer routines */
/* ------------------------------------------------------------------------- */
static int ssp_open_mixdev(struct inode *inode, struct file *file)
{
return 0;
}
static int ssp_release_mixdev(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations ssp_mixer_fops = {
owner: THIS_MODULE,
llseek: no_llseek,
ioctl: i2s_ioctl,
open: ssp_open_mixdev,
release: ssp_release_mixdev,
};
/* ------------------------------------------------------------------------- */
/* ssp_audio_reset */
/* ------------------------------------------------------------------------- */
static void ssp_audio_reset(void)
{
int flags;
local_irq_save(flags);
udelay(100);
snd_reset();
udelay(100);
snd_normal();
udelay(500); /* give the dac time to settle */
local_irq_restore(flags);
}
/* ------------------------------------------------------------------------- */
/* ssp_audio_init */
/* ------------------------------------------------------------------------- */
static void ssp_audio_init(void)
{
int flags;
/*
Note from Bob Peterson (ACD): According to EJ:
The [TI PCM1717] audio DAC for [our] PowerPC [board] is on PSC2.
It will run in i2s mode. The pin functions are documented on Page 2-34
(PDF page 80) of the MPC5200 User Manual.
I expect to run it in "Codec2 with MCLK" mode, with i2s data formatting.
See chapter 15 for PSC setup details. [pp 501-555]
We need to program the MCLK signal to have a clock of close to
11.2896 MHz (we may have to be a small bit off to make the numbers work,
the target is a 44.1 KHz sample rate times 256).
This means we'll need to set the Serial Interface Control Register to:
-first bit of first time slow of a new frame starts one bit clock cycle
after the rising edge of frame sync.
-MSB first
-Codec mode, 32-bit data
-use bit clock and frame sync generated internally from MCLK. */
local_irq_save(flags);
/* set the initial volume */
tipcm1717setregister(reg1717_vol_l, 0x0008 | reg1717_vol_ldl);
tipcm1717setregister(reg1717_vol_r, 0x0008 | reg1717_vol_ldl);
tipcm1717setregister(reg1717_mute, 0x0000);
tipcm1717setregister(reg1717_io_fmt, reg1717_io_normal);
local_irq_restore(flags);
}
/* ------------------------------------------------------------------------- */
/* original i2s routines start here */
/* ------------------------------------------------------------------------- */
static int i2s_buffers_init(void)
{
int i;
for (i = 0; i < MAX_DMA_BUFFERS; i++) {
if ((i2s_tx_bufs[i].va =
pci_alloc_consistent(NULL, BUFSIZE, &i2s_tx_bufs[i].pa)) == NULL) {
printk("Error: unable to allocate tx buffer #%d.\n",i+1);
return -1;
}
#ifdef ENABLE_RX
if ((i2s_rx_bufs[i].va =
pci_alloc_consistent(NULL, BUFSIZE, &i2s_rx_bufs[i].pa)) == NULL) {
printk("Error: unable to allocate rx buffer #%d.\n",i+1);
return -1;
}
#endif
}
return 0;
}
/* ------------------------------------------------------------------------- */
/* i2s_buffers_release */
/* ------------------------------------------------------------------------- */
static int i2s_buffers_release(void)
{
int i;
for (i = 0; i < MAX_DMA_BUFFERS; i++) {
pci_free_consistent(NULL, BUFSIZE, i2s_tx_bufs[i].va, i2s_tx_bufs[i].pa);
TaskBDRelease(txtask);
#ifdef ENABLE_RX
pci_free_consistent(NULL, BUFSIZE, i2s_rx_bufs[i].va, i2s_rx_bufs[i].pa);
TaskBDRelease(rxtask);
#endif
}
return 0;
}
/* ------------------------------------------------------------------------- */
/* i2s_tx_irq - handle transmit interrupt */
/* ------------------------------------------------------------------------- */
static void i2s_tx_irq(int irq, void *dev_id, struct pt_regs *regs)
{
int bdnum;
#ifdef DEBUG_LOST_INT
tx_int = 1;
#endif
TaskIntClear(txtask);
if (tx_assigned == tx_released) {
printk("%s: %ld:%ld - false int??\n",__FUNCTION__,tx_assigned,tx_released);
return;
}
while ((bdnum = TaskBDRelease(txtask))>=0) {
tx_released++;
if (tx_released>100*MAX_DMA_BUFFERS) { /* make sure our numbers never wrap to negative */
tx_released-=98*MAX_DMA_BUFFERS;
tx_assigned-=98*MAX_DMA_BUFFERS;
}
if (bdnum != tx_released % MAX_DMA_BUFFERS)
printk("%s: %d:%ld\n", __FUNCTION__,bdnum,tx_released % MAX_DMA_BUFFERS);
WDPRINTK("%s: released bd %d\n", __FUNCTION__,
(bdnum + MAX_DMA_BUFFERS - 1) % MAX_DMA_BUFFERS);
} /* while */
wake_up(&i2s_wait_tx);
}
#ifdef ENABLE_RX
/* ------------------------------------------------------------------------- */
/* i2s_rx_irq */
/* ------------------------------------------------------------------------- */
static void i2s_rx_irq(int irq, void *dev_id, struct pt_regs *regs)
{
int bdnum;
if (rx_assigned == rx_released) {
printk("%s: %ld:%ld - false int??\n", __FUNCTION__, rx_assigned, rx_released);
return;
}
TaskIntClear(rxtask);
bdnum = TaskBDRelease(rxtask);
rx_released++;
if (bdnum != rx_released % MAX_DMA_BUFFERS)
printk("%s: %d:%ld\n", __FUNCTION__, bdnum, rx_released % MAX_DMA_BUFFERS);
RDPRINTK("%s: released bd %d\n", __FUNCTION__,
(bdnum + MAX_DMA_BUFFERS - 1) % MAX_DMA_BUFFERS);
if (rx_assigned > rx_released) {
bdnum=TaskBDAssign(rxtask, (void *)i2s_rx_bufs[bdnum].pa, NULL, BUFSIZE,0);
RDPRINTK("%s: assigned bd %d\n", __FUNCTION__, bdnum);
}
wake_up(&i2s_wait_rx);
}
#endif
/* ------------------------------------------------------------------------- */
/**
* i2s_write:
* @file: file handle
* @buf: buffer to write (unused as data does not matter here
* @count: count of bytes
* @ppos: pointer to the position to write. No seeks allowed
*
*/
/* ------------------------------------------------------------------------- */
static ssize_t i2s_write(struct file *file, const char *buf, size_t count,
loff_t *ppos)
{
size_t cnt = count;
size_t tc;
char *ptr = (char *)buf;
int bdnum, err;
int n, flags;
if (!configured) {
printk("i2s: write when not configured.\n");
return -EINVAL;
}
/* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos) {
printk("i2s: Can't seek (pwrite) on this device.\n");
return -ESPIPE;
}
if (once) {
/*printk("i2s: First write: starting rx_tx task.\n");*/
i2s_start_rx_tx(txtask);
once = 0;
}
/*WDPRINTK("%s: count %d\n", __FUNCTION__, count);*/
while(cnt) {
/* Wait until all buffers will be released */
/*err = wait_event_interruptible(i2s_wait_tx, tx_assigned==tx_released);*/
/* Wait until the number of buffers assigned is less than the */
/* number of buffers released plus the size of the buffer queue. */
/*printk("W: Assigned: %d Released: %d\n",tx_assigned,tx_released);*/
if (tx_assigned-tx_released>=MAX_DMA_BUFFERS) {
err = wait_event_interruptible(i2s_wait_tx,
tx_assigned-tx_released<MAX_DMA_BUFFERS);
if (err) {
#ifdef DEBUG_LOST_INT
printk("%s: interrupted, %ld:%ld:%ld, tx_int %d, err %d\n",
__FUNCTION__,tx_assigned, tx_acount, tx_released, tx_int, err);
#endif
break;
}
}
/* prefill the buffers */
tc = (cnt > BUFSIZE) ? BUFSIZE : cnt;
if (copy_from_user(i2s_tx_bufs[tx_assigned % MAX_DMA_BUFFERS].va,
ptr, tc)) {
err = -EFAULT;
break;
}
i2s_tx_bufs[tx_assigned % MAX_DMA_BUFFERS].len = tc;
n = tx_assigned % MAX_DMA_BUFFERS;
tx_assigned++;
ptr += tc;
cnt -= tc;
/* Now assign the first buffer to the Bestcomm task */
save_flags(flags);cli(); /* Bestcomm can lose interrupts otherwise ??? */
#ifdef DEBUG_LOST_INT
tx_int = 0;
tx_acount++;
#endif
// Note from Bob: bd = Buffer Descriptor
bdnum = TaskBDAssign(txtask, (void *)i2s_tx_bufs[n].pa, NULL,
i2s_tx_bufs[n].len, 0);
restore_flags(flags);
if (bdnum < 0) {
printk("%s:TaskBDAssign error %d(tx stuck??)\n", __FUNCTION__, bdnum);
return -EIO;
}
WDPRINTK("%s: assigned bd %d (buf %d), tc %d\n", __FUNCTION__,
bdnum, n, tc);
}
/*WDPRINTK("%s: sent %d, err %d\n", __FUNCTION__, count - cnt, err);*/
return (count - cnt) ? (count - cnt) : err;
} /* i2s_write */
/* ------------------------------------------------------------------------- */
/**
* i2s_read:
* @file: file handle
* @buf: buffer to read
* @count: length of buffer
* @ptr: offset (no seek allowed)
*
*/
/* ------------------------------------------------------------------------- */
static ssize_t i2s_read(struct file *file, char *buf,size_t count,loff_t *ppos)
{
#ifdef ENABLE_RX
size_t cnt = count;
char *ptr = buf;
size_t bd_count;
int bdnum;
int err;
if (!configured)
return -EINVAL;
/* Can't seek (pread) on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
if (once) {
i2s_start_rx_tx(rxtask);
once = 0;
}
while (cnt) {
/* How much BDs are needed (up to MAX_DMA_BUFFERS) */
bd_count = (cnt + BUFSIZE - 1) / BUFSIZE;
if (bd_count > MAX_DMA_BUFFERS)
bd_count = MAX_DMA_BUFFERS;
rx_assigned += bd_count;
RDPRINTK("%s: cnt %d, rx_data %ld, ass %ld, rel %ld\n", __FUNCTION__, cnt,
rx_data, rx_assigned, rx_released);
/* Start reading the data */
bdnum = TaskBDAssign(rxtask,
(void *)i2s_rx_bufs[rx_released % MAX_DMA_BUFFERS].pa,
NULL, BUFSIZE, 0);
if (bdnum < 0) {
printk("%s:TaskBDAssign error %d\n", __FUNCTION__, bdnum);
return -EIO;
}
RDPRINTK("%s:assigned bd %d (buf %ld)\n", __FUNCTION__,
bdnum, rx_assigned % MAX_DMA_BUFFERS);
/* Copy the data to user as it arrives */
while (bd_count--) {
int len;
len = (cnt > BUFSIZE) ? BUFSIZE : cnt;
RDPRINTK("%s:to user:rx_data %ld, ass %ld, rel %ld\n",
__FUNCTION__, rx_data,
rx_assigned, rx_released);
/* Wait at least one BD released */
err = wait_event_interruptible(i2s_wait_rx, rx_data < rx_released);
if(err) {
goto out;
}
if (copy_to_user(ptr, i2s_rx_bufs[rx_data % MAX_DMA_BUFFERS].va, len)) {
err = -EFAULT;
goto out;
}
ptr += len;
cnt -= len;
rx_data ++;
}
}
out:
RDPRINTK("%s: read %d, err %d\n", __FUNCTION__, count - cnt, err);
return (count - cnt) ? (count - cnt) : err;
#else
return -EINVAL;
#endif
}
/* ------------------------------------------------------------------------- */
/* i2s_start_rx_tx */
/* ------------------------------------------------------------------------- */
static void i2s_start_rx_tx(int task)
{
#ifdef ENABLE_RX
if (task == rxtask) {
#ifdef RX_TASK_START
TaskStart(rxtask, TASK_AUTOSTART_ENABLE, rxtask, TASK_INTERRUPT_ENABLE);
#endif
}
#else
if (task == txtask) {
#endif
#ifdef TX_TASK_START
TaskStart(txtask, TASK_AUTOSTART_ENABLE, txtask, TASK_INTERRUPT_ENABLE);
#endif
}
psc_enable();
}
/* ------------------------------------------------------------------------- */
/* psc_enable */
/* ------------------------------------------------------------------------- */
static void psc_enable(void)
{
struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)(MPC5xxx_PSC1 +
((psc_num - 1) * 0x200));
psc->command = MPC5xxx_PSC_RX_ENABLE | MPC5xxx_PSC_TX_ENABLE;
}
/* ------------------------------------------------------------------------- */
/* psc_reset */
/* ------------------------------------------------------------------------- */
static void psc_reset(void)
{
struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)(MPC5xxx_PSC1 +
((psc_num - 1) * 0x200));
#ifdef ENABLE_RX
psc->command = MPC5xxx_PSC_RST_RX;
#endif
psc->command = MPC5xxx_PSC_RST_TX;
psc->command = MPC5xxx_PSC_SEL_MODE_REG_1;
psc->command = MPC5xxx_PSC_RST_ERR_STAT;
}
/* ------------------------------------------------------------------------- */
/* i2s_channel_setup */
/* ------------------------------------------------------------------------- */
static int i2s_channel_setup(struct i2s_ioctl *arg)
{
struct mpc5xxx_psc *psc;
struct mpc5xxx_gpio *gpio = (struct mpc5xxx_gpio *)MPC5xxx_GPIO;
struct mpc5xxx_cdm *cdm = (struct mpc5xxx_cdm *)MPC5xxx_CDM;
TaskSetupParamSet_t i2s_setup_tx;
#ifdef ENABLE_RX
TaskSetupParamSet_t i2s_setup_rx;
#endif
if (arg->psc_num < 1 || arg->psc_num > 3) {
printk("i2s: Invalid psc specified during channel_setup.\n");
return -EINVAL;
}
psc_num = arg->psc_num;
switch (psc_num) {
case 1:
initiator_tx = INITIATOR_PSC1_TX;
#ifdef ENABLE_RX
initiator_rx = INITIATOR_PSC1_RX;
#endif
cdm->mclken_div_psc1 |= (0x8000 | arg->mclk_div);
break;
case 2:
initiator_tx = INITIATOR_PSC2_TX;
#ifdef ENABLE_RX
initiator_rx = INITIATOR_PSC2_RX;
#endif
cdm->mclken_div_psc2 = (0x8000 | arg->mclk_div);
break;
case 3:
initiator_tx = INITIATOR_PSC3_TX;
#ifdef ENABLE_RX
initiator_rx = INITIATOR_PSC3_RX;
#endif
cdm->mclken_div_psc3 |= (0x8000 | arg->mclk_div);
break;
}
psc = (struct mpc5xxx_psc *)(MPC5xxx_PSC1 + ((psc_num - 1) * 0x200));
/* disable Tx & Rx */
psc->command = (MPC5xxx_PSC_TX_DISABLE | MPC5xxx_PSC_RX_DISABLE);
gpio->port_config &= ~(0x7 << ((psc_num - 1) * 4));
gpio->port_config &= 0xcfffffff; /* Turn off ALTS */
if (arg->mclk) {
gpio->port_config|=(0x7 << ((arg->psc_num-1)*4)); /* CODEC with MCLK */
} else {
gpio->port_config|=(0x6 << ((arg->psc_num-1)*4)); /* CODEC without MCLK */
}
cdm->clk_enables |= (1 << (arg->psc_num + 4)); /* PSC clock enable */
/* reset PSC */
psc_reset();
psc->mode = 0;
psc->rfalarm = 0x120; /* alarm threshold level */
psc->tfalarm = 0x120;
psc->rfcntl = 4; /* granularity */
psc->tfcntl = 4;
psc->mpc5xxx_psc_imr = 0; /* disable interrupts */
/* Configure codec parameters (MSB first) */
switch (arg->data_width) {
case 8:
psc->sicr = 0x01000000;
psc->ctur = 0x7; /* 8 bits per frame */
break;
case 16:
psc->sicr = 0x02000000; /* 004xxxxx=multi word*/
psc->ctur = 0xf; /* 0xf; 16 bits per frame */
break;
case 24:
psc->sicr = 0x07000000;
psc->ctur = 0x17; /* 24 bits per frame */
break;
case 32:
psc->sicr = 0x0f000000;
psc->ctur = 0x1f; /* 32 bits per frame: set frame width to 32 sclks */
break;
default:
printk("Invalid data_width specified.\n");
return -EINVAL;
}
psc->ccr = (arg->frsync_div*0x100) + arg->bclk_div;
/* Bob note: I think ccr must be a write-only register. It comes back 0. */
/* However, the databook seems to indicate r/w. Not sure why. */
/* DTS = 1, Data MSB first, use 5200 clk and frame sync, multiword.
* Frame sync active low, data sampled on CLK high.
*/
psc->sicr |= /*DELAY_TIME_SLOT |*/ MULTIWD_ENABLE | CLK_POL_RISING;
if (arg->master) /* Master mode - PSC will drive SCLK and LRCK */
psc->sicr |= GEN_CLK_INT;
/* reset PSC again */
psc_reset();
/* setup the tasks */
memset(&i2s_setup_tx, 0, sizeof(i2s_setup_tx));
i2s_setup_tx.Initiator = (MPC5200Initiator_t)(initiator_tx);
i2s_setup_tx.NumBD = MAX_DMA_BUFFERS;
i2s_setup_tx.Size.MaxBuf = BUFSIZE;
i2s_setup_tx.StartAddrDst = (volatile u32)&(psc->tfdata);
i2s_setup_tx.IncrSrc = arg->data_width/8; /* added by Bob: was 4 */
i2s_setup_tx.IncrDst = 0;
i2s_setup_tx.SzSrc = arg->data_width/8; /* was 4 */
i2s_setup_tx.SzDst = arg->data_width/8; /* was 4 */
txtask = TaskSetup(TASK_GEN_TX_BD, &i2s_setup_tx);
if (request_irq(MPC5xxx_SDMA_IRQ_BASE + txtask, i2s_tx_irq,
SA_INTERRUPT, "i2s tx dma", NULL)) {
printk(KERN_ERR "i2s: SDMA tx irq allocation failed\n");
return -EINVAL;
}
#ifdef ENABLE_RX
memset(&i2s_setup_rx, 0, sizeof(i2s_setup_rx));
i2s_setup_rx.Initiator = (MPC5200Initiator_t)(initiator_rx);
i2s_setup_rx.NumBD = MAX_DMA_BUFFERS;
i2s_setup_rx.Size.MaxBuf = BUFSIZE;
i2s_setup_rx.StartAddrSrc = (volatile u32)&(psc->rfdata);
i2s_setup_rx.IncrDst = 4;
i2s_setup_rx.IncrSrc = 0;
i2s_setup_rx.SzDst = 4;
rxtask = TaskSetup(TASK_GEN_RX_BD, &i2s_setup_rx);
if (request_irq(MPC5xxx_SDMA_IRQ_BASE + rxtask, i2s_rx_irq,
SA_INTERRUPT, "i2s rx dma", NULL)) {
printk(KERN_ERR "i2s: SDMA rx irq allocation failed\n");
return -EINVAL;
}
#endif
configured = 1;
return 0;
}/* i2s_channel_setup */
/* ------------------------------------------------------------------------- */
/**
* i2s_ioctl:
* @inode: inode of the device
* @file: file handle to the device
* @arg: argument pointer
*/
/* ------------------------------------------------------------------------- */
static int i2s_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
long val;
int ret;
struct i2s_ioctl i2s_arg;
static int *intpnd,*intmsk;
switch (cmd) {
case I2S_SET_CHANNEL:
if (copy_from_user (&i2s_arg, (struct i2s_ioctl *)arg, sizeof(i2s_arg)))
return -EFAULT;
ret = i2s_channel_setup(&i2s_arg);
break;
case I2S_DEBUG:
intpnd=(int *)(0xf0001214);
intmsk=(int *)(0xf0001218);
printk("txtask=%d INTPND=%08X INTMSK=%08X\n",
txtask,(unsigned int)*intpnd,(unsigned int)*intmsk);
printk("Assigned=%ld Released=%ld\n",tx_assigned,tx_released);
break;
case I2S_BEEP:
if (arg)
beep_duration=(int)arg;
else
beep_duration=BEEP_DURATION; /* in 100ths of a second */
beeper_on();
break;
case I2S_RESET:
ssp_audio_reset();
break;
case SNDCTL_DSP_STEREO:
ret = get_user(val, (int *) arg);
if (ret)
return ret;
/* Simple standard DACs are stereo only */
ret = (val == 0) ? -EINVAL : 1;
return put_user(ret, (int *) arg);
case SNDCTL_DSP_CHANNELS:
case SOUND_PCM_READ_CHANNELS:
/* Simple standard DACs are stereo only */
return put_user(2, (long *) arg);
case SNDCTL_DSP_SPEED:
case SOUND_PCM_READ_RATE:
/* We assume the clock doesn't change */
return put_user(sample_rate, (long *) arg);
case SNDCTL_DSP_SETFMT:
if (arg!=AFMT_S16_BE) { /* if not signed 16-bit big-endian */
printk("SNDCTL_DSP_SETFMT: request for 0x%08X rejected.\n",arg);
return -1;
}
/* Simple standard DACs are 16-bit only */
return put_user(16, (long *) arg);
case SNDCTL_DSP_GETFMTS:
/* Simple standard DACs are 16-bit only */
return put_user(16, (long *) arg);
case SNDCTL_DSP_SYNC:
wait_event_interruptible(i2s_wait_tx,tx_assigned==tx_released);
return 0;
case SOUND_MIXER_WRITE_VOLUME:
tipcm1717setregister(reg1717_vol_l, (unsigned short)arg | reg1717_vol_ldl);
tipcm1717setregister(reg1717_vol_r, (unsigned short)arg | reg1717_vol_ldl);
return 0;
case SNDCTL_DSP_GETOSPACE:
{
audio_buf_info inf = { 0, };
int i;
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
inf.fragments=MAX_DMA_BUFFERS-(tx_assigned-tx_released);
inf.bytes = BUFSIZE*(MAX_DMA_BUFFERS-(tx_assigned-tx_released));
inf.fragstotal = MAX_DMA_BUFFERS;
inf.fragsize = BUFSIZE;
return copy_to_user((void *)arg, &inf, sizeof(inf));
}
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
put_user(BUFSIZE*(tx_assigned-tx_released), (int *)arg);
return 0;
default:
ret = -EINVAL;
break;
}
return ret;
}
/* ------------------------------------------------------------------------- */
/**
* i2s_open:
* @inode: inode of device
* @file: file handle to device
*/
/* ------------------------------------------------------------------------- */
static int i2s_open(struct inode *inode, struct file *file)
{
if(test_and_set_bit(0, &i2s_is_open))
return -EBUSY;
i2s_channel_setup(&defaults);
ssp_audio_init();
i2s_is_open = 1;
tx_assigned = tx_released = 0;
#ifdef ENABLE_RX
rx_assigned = rx_released = 0;
rx_data = 0;
#endif
#ifdef DEBUG_LOST_INT
tx_acount = 0;
#endif
once = 1;
return 0;
}
/* ------------------------------------------------------------------------- */
/**
* i2s_release
* @inode: inode to board
* @file: file handle to board
*/
/* ------------------------------------------------------------------------- */
static int i2s_release(struct inode *inode, struct file *file)
{
wait_event_interruptible(i2s_wait_tx, tx_assigned == tx_released);
psc_reset();
#ifdef ENABLE_RX
if (rxtask) {
TaskStop(rxtask);
free_irq(MPC5xxx_SDMA_IRQ_BASE + rxtask, NULL);
rxtask = 0;
}
#endif
if (txtask) {
TaskStop(txtask);
free_irq(MPC5xxx_SDMA_IRQ_BASE + txtask, NULL);
TaskBDReset(txtask);
txtask = 0;
}
configured = 0;
clear_bit(0, &i2s_is_open);
return 0;
}
/* ------------------------------------------------------------------------- */
/* i2s_poll */
/* ------------------------------------------------------------------------- */
static unsigned int i2s_poll(struct file *file, poll_table *wait)
{
int ret = 0;
if (configured) {
ret = (POLLIN | POLLRDNORM);
}
return ret;
}
static struct file_operations i2s_fops = {
owner: THIS_MODULE,
llseek: no_llseek,
read: i2s_read,
write: i2s_write,
ioctl: i2s_ioctl,
open: i2s_open,
release: i2s_release,
poll: i2s_poll,
};
static struct miscdevice i2s_miscdev=
{
I2S_MINOR,
"I2S",
&i2s_fops
};
/* ------------------------------------------------------------------------- */
/*
* cleanup_module:
*/
/* ------------------------------------------------------------------------- */
static void __exit i2s_exit(void)
{
misc_deregister(&i2s_miscdev);
i2s_buffers_release();
TaskStop(txtask);
free_irq(MPC5xxx_SDMA_IRQ_BASE + txtask, NULL);
#ifdef ENABLE_RX
TaskStop(rxtask);
free_irq(MPC5xxx_SDMA_IRQ_BASE + rxtask, NULL);
#endif
}
/* ------------------------------------------------------------------------- */
/*
* i2s_init:
*/
/* ------------------------------------------------------------------------- */
static int __init i2s_init(void)
{
static int ret;
/*struct mpc5xxx_intr *int_ctrl;*/
/*sdma_regs *sdma = (sdma_regs *)MPC5xxx_SDMA;*/
struct mpc5xxx_cdm *cdm = (struct mpc5xxx_cdm *)MPC5xxx_CDM;
printk("i2s.c: audio device driver for PowerPC.\n");
cdm->pci_clk_sel&=0xfffffffc; /* Speed up the pci clock by factor of 2*/
udelay(50); /* wait just a little */
ret = misc_register(&i2s_miscdev);
if (ret) {
printk(KERN_ERR "i2s: can't misc_register on minor=%d\n", I2S_MINOR);
goto out;
}
i2s_buffers_init();
defaults.psc_num=2;
defaults.mclk_div=0x2e;
defaults.mclk=1;
defaults.data_width=16;
defaults.frsync_div=0x1f;
defaults.bclk_div=7;
defaults.master=1;
printk("We have audio for mpc5200!\n");
mixer_dev_id = register_sound_mixer(&ssp_mixer_fops, -1);
/*printk( KERN_INFO "Mixer registered. id=%d\n",mixer_dev_id);*/
/*printk(KERN_INFO "Initializing pcm1717 chip.\n");*/
ssp_audio_reset();
ssp_audio_init();
/* int_ctrl=(struct mpc5xxx_intr *)MPC5xxx_INTR; * MBAR_INT_CTRL */
/* int_ctrl->pimsk &= (~(PIMSK_BESTCOMM)); */
/* Bob: Assume that u-boot has initialized Bestcomm for the purposes */
/* of dmaing to and from the ethernet FEC. */
TasksInitAPI((uint8 *)MPC5xxx_MBAR); /* Still needed */
/* sdma->taskBar = MPC5xxx_SRAM; */
/* TasksLoadImage(sdma); */
ret = 0;
printk(KERN_INFO "MPC5200 I2S driver (%d BDs x %d bytes).\n",
MAX_DMA_BUFFERS, BUFSIZE);
/* --------------------------------------------------------------------- */
/* Install our timer interrupt routine */
/* --------------------------------------------------------------------- */
queue_task(&Task, &tq_timer);
out:
return ret;
}
/* ------------------------------------------------------------------------ */
/* timer_int - Timer Interrupt */
/* ------------------------------------------------------------------------ */
static void timer_int(void *nothing)
{
if (beep_duration>0) {
beep_duration--; /* in 100ths of a second */
if (!beep_duration)
beeper_off();
}
if (waitqueue_active(&WaitQ)) /* if Cleanup wants us to die */
wake_up(&WaitQ); /* Now cleanup_module can return */
else
queue_task(&Task, &tq_timer); /* put ourselves back in the task queue. */
}
module_init(i2s_init);
module_exit(i2s_exit);
MODULE_DESCRIPTION("Simple driver for the MPC5200 PSC in CODEC I2S mode.\n");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2005-09-16 16:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-09-16 13:29 I2S MPC5200 Arnaud Carer
2005-09-16 16:16 ` Bob Peterson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).