/** * brief omap24xx_uart_set_parms * writes values into requested UART register * param uart_no * param uart_set * * return */ int omap24xx_uart_set_parms(int uart_no, struct uart_setparm *uart_set) { u32 uart_base; u8 lcr_data; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); FN_OUT(EPERM); return -EPERM; } spin_lock(&(ui[uart_no].uart_lock)); uart_base = omap24xx_uart_base_v(uart_no); lcr_data = readb(uart_base + REG_LCR); outb(uart_set->lcr, uart_base + REG_LCR); outb(uart_set->reg_data, uart_base + uart_set->reg); /* Restore LCR data */ outb(lcr_data, uart_base + REG_LCR); spin_unlock(&(ui[uart_no].uart_lock)); return 0; FN_OUT(0); }
/* The work item handler */ static void audio_dsr_handler(unsigned long inData) { void *data = (void *)inData; struct audio_isr_work_item *work = data; audio_stream_t *s = (work->s); int sound_curr_lch = work->current_lch; u16 ch_status = work->ch_status; FN_IN; DPRINTK("lch=%d,status=0x%x, data=%p as=%p\n", sound_curr_lch, ch_status, data, s); if (AUDIO_QUEUE_EMPTY(s)) { ERR("Interrupt(%d) for empty queue(h=%d, T=%d)???\n", sound_curr_lch, s->dma_q_head, s->dma_q_tail); ERR("nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d\n", s->nbfrags, s->pending_frags, s->usr_head, s->dma_head, s->dma_tail); FN_OUT(-1); return; } AUDIO_INCREMENT_HEAD(s); /* Empty the queue */ /* Try to fill again */ audio_dma_callback(sound_curr_lch, ch_status, s); FN_OUT(0); }
/*************************************************************************************** * * Buffer creation/destruction * **************************************************************************************/ int audio_setup_buf(audio_stream_t * s) { int frag; int dmasize = 0; char *dmabuf = NULL; dma_addr_t dmaphys = 0; FN_IN; if (s->buffers) { FN_OUT(1); return -EBUSY; } s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); if (!s->buffers) goto err; memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); for (frag = 0; frag < s->nbfrags; frag++) { audio_buf_t *b = &s->buffers[frag]; /* * Let's allocate non-cached memory for DMA buffers. * We try to allocate all memory at once. * If this fails (a common reason is memory fragmentation), * then we allocate more smaller buffers. */ if (!dmasize) { dmasize = (s->nbfrags - frag) * s->fragsize; do { dmabuf = dma_alloc_coherent(NULL, dmasize, &dmaphys, 0); if (!dmabuf) dmasize -= s->fragsize; } while (!dmabuf && dmasize); if (!dmabuf) goto err; b->master = dmasize; memzero(dmabuf, dmasize); } b->data = dmabuf; b->dma_addr = dmaphys; dmabuf += s->fragsize; dmaphys += s->fragsize; dmasize -= s->fragsize; } s->usr_head = s->dma_head = s->dma_tail = 0; AUDIO_QUEUE_INIT(s); s->started = 0; s->bytecount = 0; s->fragcount = 0; init_completion(&s->wfc); s->wfc.done = s->nbfrags; FN_OUT(0); return 0; err: audio_discard_buf(s); FN_OUT(1); return -ENOMEM; }
int omap_request_alsa_sound_dma(int device_id, const char *device_name, void *data, int **channels) { int i, err = 0; int *chan = NULL; FN_IN; if (unlikely((NULL == channels) || (NULL == device_name))) { BUG(); return -EPERM; } /* Try allocate memory for the num channels */ *channels = kmalloc(sizeof(int) * nr_linked_channels, GFP_KERNEL); chan = *channels; if (NULL == chan) { ERR("No Memory for channel allocs!\n"); FN_OUT(-ENOMEM); return -ENOMEM; } spin_lock(&dma_list_lock); for (i = 0; i < nr_linked_channels; i++) { err = omap_request_dma(device_id, device_name, sound_dma_irq_handler, data, &chan[i]); /* Handle Failure condition here */ if (err < 0) { int j; for (j = 0; j < i; j++) omap_free_dma(chan[j]); spin_unlock(&dma_list_lock); kfree(chan); *channels = NULL; ERR("Error in requesting channel %d=0x%x\n", i, err); FN_OUT(err); return err; } } /* Chain the channels together */ if (!cpu_is_omap15xx()) omap_sound_dma_link_lch(data); spin_unlock(&dma_list_lock); FN_OUT(0); return 0; }
/** * brief omap24xx_uart_get_speed * reads DLL and DLH register values and * calculates UART speed. * param uart_no * param speed * * return */ int omap24xx_uart_get_speed(int uart_no, int *speed) { u32 uart_base; u8 reg; u8 dll, dlh; u16 divisor; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); return -EPERM; } uart_base = omap24xx_uart_base_v(uart_no); spin_lock(&(ui[uart_no].uart_lock)); reg = LCR_MODE2; outb(reg, uart_base + REG_LCR); dll = readb(uart_base + REG_DLL); dlh = readb(uart_base + REG_DLH); divisor = (dlh << 8) + dll; if (!divisor) { printk(KERN_WARNING "UART: DLL and DLH read error\n"); return -EPERM; } *speed = (BASE_CLK) / 16 * divisor; spin_unlock(&(ui[uart_no].uart_lock)); return 0; FN_OUT(0); }
/* The call back that handles buffer stuff */ static void audio_dma_callback(int lch, u16 ch_status, void *data) { audio_stream_t *s = data; audio_buf_t *b = &s->buffers[s->dma_tail]; FN_IN; if (s->dma_spinref > 0) { s->dma_spinref--; } else if (!s->buffers) { printk(KERN_CRIT "omap_audio: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; s->dma_spinref = -s->dma_spinref; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) complete(&s->wfc); else s->pending_frags++; wake_up(&s->wq); } audio_process_dma(s); FN_OUT(0); return; }
/* * omap24xxx_timeout_isr * Looks at DMA destination register and calls receive * callback if the destination register is the same on * two timer isr. */ static void omap24xx_timeout_isr(unsigned long uart_no) { u32 w = 0; int lch; int curr_pos; static int prev_pos = 0; struct omap24xx_uart *hsuart = &ui[uart_no]; FN_IN; lch = hsuart->rx_dma_channel; curr_pos = omap_get_dma_dst_pos(lch); if ((curr_pos == prev_pos) && (curr_pos != hsuart->rx_buf_dma_phys)) { omap_stop_dma(lch); w = OMAP_DMA_CSR_REG(lch); uart_rx_dma_callback(lch, w, uart_cb[hsuart->uart_no].dev); prev_pos = 0; } else { prev_pos = curr_pos; mod_timer(&hsuart->timer, jiffies + msecs_to_jiffies(hsuart->timeout)); } FN_OUT(0); }
/*************************************************************************************** * * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive, * we need to prime it * **************************************************************************************/ void audio_prime_rx(audio_state_t * state) { audio_stream_t *is = state->input_stream; FN_IN; if (state->need_tx_for_rx) { /* * With some codecs like the Philips UDA1341 we must ensure * there is an output stream at any time while recording since * this is how the UDA1341 gets its clock from the SA1100. * So while there is no playback data to send, the output DMA * will spin with all zeroes. We use the cache flush special * area for that. */ state->output_stream->spin_idle = 1; audio_process_dma(state->output_stream); } is->pending_frags = is->nbfrags; init_completion(&is->wfc); is->wfc.done = 0; is->active = 1; audio_process_dma(is); FN_OUT(0); return; }
static void audio_ldm_device_unregister(void) { extern void dsp_public_device_unregister(struct device *device); FN_IN; dsp_public_device_unregister(&audio_device_ldm); FN_OUT(0); }
/*************************************************************************************** * * Clear any pending transfers * **************************************************************************************/ void omap_clear_sound_dma(audio_stream_t * s) { FN_IN; omap_clear_dma(s->lch[s->dma_q_head]); FN_OUT(0); return; }
static void audio_ldm_driver_unregister(void) { extern void dsp_public_driver_unregister(struct device_driver *driver); FN_IN; dsp_public_driver_unregister(&audio_driver_ldm); FN_OUT(0); }
/*************************************************************************************** * * DMA related functions * **************************************************************************************/ static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, u_int dma_size) { int dt = 0x1; /* data type 16 */ int cen = 32; /* Stereo */ int cfn = dma_size / (2 * cen); unsigned long dest_start; int dest_port = 0; int sync_dev = 0; FN_IN; if (cpu_is_omap16xx()) { dest_start = (OMAP1610_MCBSP1_BASE + 0x806); dest_port = OMAP_DMA_PORT_MPUI; } if (cpu_is_omap24xx()) { dest_start = AUDIO_MCBSP_DATAWRITE; sync_dev = AUDIO_DMA_TX; } omap_set_dma_dest_params(channel, dest_port, OMAP_DMA_AMODE_CONSTANT, dest_start, 0, 0); omap_set_dma_src_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0); omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, 0); FN_OUT(0); return 0; }
static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, u_int dma_size) { int dt = 0x1; /* data type 16 */ int cen = 16; /* mono */ int cfn = dma_size / (2 * cen); unsigned long src_start; int src_port = 0; int sync_dev = 0; int src_sync = 0; FN_IN; if (cpu_is_omap16xx()) { src_start = (OMAP1610_MCBSP1_BASE + 0x802); src_port = OMAP_DMA_PORT_MPUI; } if (cpu_is_omap24xx()) { src_start = AUDIO_MCBSP_DATAREAD; sync_dev = AUDIO_DMA_RX; src_sync = 1; } omap_set_dma_src_params(channel, src_port, OMAP_DMA_AMODE_CONSTANT, src_start, 0, 0); omap_set_dma_dest_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0); omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, src_sync); FN_OUT(0); return 0; }
/* * Clear any pending transfers */ void omap_clear_alsa_sound_dma(struct audio_stream *s) { FN_IN; omap_clear_dma(s->lch[s->dma_q_head]); FN_OUT(0); return; }
int omap_free_sound_dma(void *data, int **channels) { int i; int *chan = NULL; FN_IN; if (unlikely(NULL == channels)) { BUG(); return -EPERM; } if (unlikely(NULL == *channels)) { BUG(); return -EPERM; } chan = (*channels); if (!cpu_is_omap1510()) omap_sound_dma_unlink_lch(data); for (i = 0; i < nr_linked_channels; i++) { int cur_chan = chan[i]; omap_stop_dma(cur_chan); omap_free_dma(cur_chan); } kfree(*channels); *channels = NULL; FN_OUT(0); return 0; }
/* * Initializes TSC 2102 and playback target. */ void snd_omap_init_mixer(void) { FN_IN; init_playback_targets(); FN_OUT(0); }
/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to the * work item */ static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data) { int dma_status = ch_status; audio_stream_t *s = (audio_stream_t *) data; FN_IN; #ifdef IRQ_TRACE xyz[h++] = '0' + sound_curr_lch; if (h == MAX_UP - 1) { printk("%s-", xyz); h = 0; } #endif DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch, ch_status, dma_status, data); if (dma_status) { if (cpu_is_omap16xx() && (dma_status & (DCSR_ERROR))) OMAP_DMA_CCR_REG(sound_curr_lch) &= ~DCCR_EN; ERR("DCSR_ERROR!\n"); FN_OUT(-1); return; } if (AUDIO_QUEUE_LAST(s)) audio_stop_dma(s); /* Start the work item - we ping pong the work items */ if (!work_item_running) { work1.current_lch = sound_curr_lch; work1.ch_status = ch_status; work1.s = s; /* schedule tasklet 1 */ tasklet_schedule(&audio_isr_work1); work_item_running = 1; } else { work2.current_lch = sound_curr_lch; work2.ch_status = ch_status; work2.s = s; /* schedule tasklet 2 */ tasklet_schedule(&audio_isr_work2); work_item_running = 0; } FN_OUT(0); return; }
static void omap1610_tsc2101_write(u8 address, u16 data) { DPRINTK(__FUNCTION__ ": addr 0x%02x, data 0x%04x\n", address, data); /* CS = 1, 16 bit transfer, write-only */ omap1610_uwire_data_transfer(LEAVE_CS | 1, (PAGE2_AUDIO_CODEC_REGISTERS | (address << 5)), 16, 0); omap1610_uwire_data_transfer(1, data, 16, 0); FN_OUT(0); }
/*************************************************************************************** * * DMA channel requests Freeing * **************************************************************************************/ static void omap_sound_dma_unlink_lch(void *data) { audio_stream_t *s = (audio_stream_t *) data; int *chan = s->lch; int i; FN_IN; if (!s->linked) { FN_OUT(1); return; } for (i = 0; i < nr_linked_channels; i++) { int cur_chan = chan[i]; int nex_chan = ((nr_linked_channels - 1 == i) ? chan[0] : chan[i + 1]); omap_dma_unlink_lch(cur_chan, nex_chan); } s->linked = 0; FN_OUT(0); }
/** * brief omap24xx_uart_get_parms * reads requested register data * param uart_no * param data * param reg * param lcr_mode * * return */ int omap24xx_uart_get_parms(int uart_no, u8 * data, u8 reg, u8 lcr_mode) { u32 uart_base; u8 lcr_data; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); FN_OUT(EPERM); return -EPERM; } uart_base = omap24xx_uart_base_v(uart_no); lcr_data = readb(uart_base + REG_LCR); outb(lcr_mode, uart_base + REG_LCR); *data = readb(uart_base + reg); /* Restore LCR data */ outb(lcr_data, uart_base + REG_LCR); return 0; FN_OUT(0); }
static int audio_start_dma_chain(audio_stream_t * s) { int channel = s->lch[s->dma_q_head]; FN_IN; if (!s->started) { s->hw_stop(); /* stops McBSP Interface */ omap_start_dma(channel); s->started = 1; s->hw_start(); /* start McBSP interface */ } /* else the dma itself will progress forward with out our help */ FN_OUT(0); return 0; }
/* * ISRs have to be short and smart.. * Here we call alsa handling, after some error checking */ static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data) { int dma_status = ch_status; struct audio_stream *s = (struct audio_stream *) data; FN_IN; /* some register checking */ DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch, ch_status, dma_status, data); if (dma_status & (DCSR_ERROR)) { omap_stop_dma(sound_curr_lch); ERR("DCSR_ERROR!\n"); FN_OUT(-1); return; } if (ch_status & DCSR_END_BLOCK) callback_omap_alsa_sound_dma(s); FN_OUT(0); return; }
/** * brief uart_tx_dma_callback * * param lch * param ch_status * param data */ static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) { FN_IN; if (lch == ui[UART1].tx_dma_channel) { uart_cb[UART1].uart_tx_dma_callback(lch, ch_status, data); ui[UART1].tx_buf_state = FREE; } else if (lch == ui[UART2].tx_dma_channel) { uart_cb[UART2].uart_tx_dma_callback(lch, ch_status, data); ui[UART2].tx_buf_state = FREE; } else if (lch == ui[UART3].tx_dma_channel) { uart_cb[UART3].uart_tx_dma_callback(lch, ch_status, data); ui[UART3].tx_buf_state = FREE; } FN_OUT(0); }
static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, u_int dma_size) { int dt = 0x1; /* data type 16 */ int cen = 32; /* stereo */ int cfn = dma_size / (2 * cen); FN_IN; omap_set_dma_src_params(channel, 0x05, 0x00, (OMAP1510_MCBSP1_BASE + 0x02), 0, 0); omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0); omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0); FN_OUT(0); return 0; }
/** * brief omap24xx_uart_isr * Identifes the source of interrupt(UART1, UART2, UART3) * and reads respective IIR register data. * Sends IIR data to the user driver. * param irq * param dev_id * param regs */ static irqreturn_t omap24xx_uart_isr(int irq, void *dev_id) { u8 iir_data; /* regular IRQ */ u8 ssr_data; /* wake-up IRQ */ u32 uart_base; int uart_no; FN_IN; switch (irq) { case INT_24XX_UART1_IRQ: uart_base = omap24xx_uart_base_v(UART1); uart_no = UART1; break; case INT_24XX_UART2_IRQ: uart_base = omap24xx_uart_base_v(UART2); uart_no = UART2; break; case INT_24XX_UART3_IRQ: uart_base = omap24xx_uart_base_v(UART3); uart_no = UART3; break; default: printk("UART interrupt from unknown source\n"); return IRQ_NONE; } /* We got an inerrupt for our UART. At this time we do not know if it * was a normal interrupt or a wake-up interrupt. Therefore we need to * enable the clocks first as they might be off if this is a wake-up. */ omap24xx_uart_clk_enable(uart_no); /* Check for wake-up IRQ. */ ssr_data = readb(uart_base + REG_SSR); /* if IIR indicates RHR, start dma */ iir_data = readb(uart_base + REG_IIR); uart_cb[uart_no].int_callback(iir_data, ssr_data, dev_id); omap24xx_uart_clk_disable(uart_no); FN_OUT(0); return IRQ_HANDLED; }
/*************************************************************************************** * * Stop all the DMA channels of the stream * **************************************************************************************/ void audio_stop_dma(audio_stream_t * s) { int *chan = s->lch; int i; FN_IN; if (unlikely(NULL == chan)) { BUG(); return; } for (i = 0; i < nr_linked_channels; i++) { int cur_chan = chan[i]; omap_stop_dma(cur_chan); } s->started = 0; FN_OUT(0); return; }
/* * Stop all the DMA channels of the stream */ void omap_stop_alsa_sound_dma(struct audio_stream *s) { int *chan = s->lch; int i; FN_IN; if (unlikely(NULL == chan)) { BUG(); return -1; } for (i = 0; i < nr_linked_channels; i++) { int cur_chan = chan[i]; omap_stop_dma(cur_chan); } s->started = 0; FN_OUT(0); return 0; }
/*************************************************************************************** * * Reset the audio buffers * **************************************************************************************/ void audio_reset(audio_stream_t * s) { FN_IN; if (s->buffers) { audio_stop_dma(s); s->buffers[s->dma_head].offset = 0; s->buffers[s->usr_head].offset = 0; s->usr_head = s->dma_head; s->pending_frags = 0; init_completion(&s->wfc); s->wfc.done = s->nbfrags; } s->active = 0; s->stopped = 0; s->started = 0; FN_OUT(0); return; }
/*************************************************************************************** * * Get the dma posn * **************************************************************************************/ u_int audio_get_dma_pos(audio_stream_t * s) { audio_buf_t *b = &s->buffers[s->dma_tail]; u_int offset; FN_IN; if (b->dma_ref) { offset = omap_get_dma_src_pos(s->lch[s->dma_q_head]) - b->dma_addr; if (offset >= s->fragsize) offset = s->fragsize - 4; } else if (s->pending_frags) { offset = b->offset; } else { offset = 0; } FN_OUT(offset); return offset; }
static void tsc2101_update(int flag, int val) { u16 volume; FN_IN; switch (flag) { case SET_VOLUME: /* Convert 0 -> 100 volume to 0x30 -> 0x7f volume range */ volume = ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; omap1610_tsc2101_write(TSC2101_DAC_GAIN_CTRL,(volume<<8)|volume); break; case SET_LINE: break; } FN_OUT(0); }