예제 #1
0
/**
 * 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);
}
예제 #2
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);

}
예제 #3
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;
}
예제 #4
0
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;
}
예제 #5
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);
}
예제 #6
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;
}
예제 #7
0
/*
 * 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);
}
예제 #8
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;
}
예제 #9
0
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);
}
예제 #10
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;
}
예제 #11
0
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);
}
예제 #12
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;
}
예제 #13
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;
}
예제 #14
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;
}
예제 #15
0
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;
}
예제 #16
0
/*
 * Initializes TSC 2102 and playback target.
 */
void snd_omap_init_mixer(void)
{
	FN_IN;

	init_playback_targets();

	FN_OUT(0);
}
예제 #17
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;
}
예제 #18
0
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);
}
예제 #19
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);
}
예제 #20
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);
}
예제 #21
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;
}
예제 #22
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;
}
예제 #23
0
/**
 * 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);
}
예제 #24
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;
}
예제 #25
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;
}
예제 #26
0
/***************************************************************************************
 *
 * 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;
}
예제 #27
0
/*
 * 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;
}
예제 #28
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;
}
예제 #29
0
/***************************************************************************************
 *
 * 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;
}
예제 #30
0
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);
}