/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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;