/* * linux/drivers/sound/Jz_i2s.c * * JzSOC On-Chip I2S audio driver. * * Copyright (C) 2005 by Junzheng Corp. * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using * dma channel 4&3,noah is tested. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Because the normal application of AUDIO devices are focused on Little_endian, * then we only perform the little endian data format in driver. * */ #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sound_config.h" #if defined(CONFIG_I2S_DLV) #include "jzdlv.h" #endif #define DPRINTK(args...) printk(args) #define DMA_ID_I2S_TX DMA_ID_AIC_TX #define DMA_ID_I2S_RX DMA_ID_AIC_RX #define NR_I2S 2 #define JZCODEC_RW_BUFFER_SIZE 5 #define JZCODEC_RW_BUFFER_TOTAL 4 #define USE_NONE 1 #define USE_MIC 2 #define USE_LINEIN 3 typedef struct hpvol_shift_s { int hpvol; int shift; } hpvol_shift_t; mixer_info info; _old_mixer_info old_info; int codec_volue_shift; hpvol_shift_t hpvol_shift_table[72]; int abnormal_data_count; unsigned long i2s_clk; void (*set_codec_mode)(void) = NULL; void (*clear_codec_mode)(void) = NULL; void (*set_codec_gpio_pin)(void) = NULL; void (*each_time_init_codec)(void) = NULL; int (*set_codec_startup_param)(void) = NULL; void (*set_codec_volume_table)(void) = NULL; void (*set_codec_record)(int mode) = NULL; void (*set_codec_replay)(void) = NULL; void (*set_codec_replay_record)(int mode) = NULL; void (*turn_on_codec)(void) = NULL; void (*turn_off_codec)(void) = NULL; void (*set_codec_speed)(int rate) = NULL; void (*reset_codec)(void) = NULL; void (*codec_mixer_old_info_id_name)(void) = NULL; void (*codec_mixer_info_id_name)(void) = NULL; void (*set_codec_bass)(int val) = NULL; void (*set_codec_volume)(int val) = NULL; void (*set_codec_mic)(int val) = NULL; void (*set_codec_line)(int val) = NULL; void (*i2s_resume_codec)(void) = NULL; void (*i2s_suspend_codec)(int wr,int rd) = NULL; void (*init_codec_pin)(void) = NULL; void (*set_codec_some_func)(void) = NULL; void (*clear_codec_record)(void) = NULL; void (*clear_codec_replay)(void) = NULL; void (*set_replay_hp_or_speaker)(void) = NULL; void (*set_codec_direct_mode)(void) = NULL; void (*clear_codec_direct_mode)(void) = NULL; void (*set_codec_linein2hp)(void) = NULL; void (*clear_codec_linein2hp)(void) = NULL; static int jz_audio_rate; static int jz_audio_format; static int jz_audio_volume; static int jz_audio_channels; static int jz_audio_b; /* bits expand multiple */ static int jz_audio_fragments; /* unused fragment amount */ static int jz_audio_fragstotal; static int jz_audio_fragsize; static int jz_audio_speed; static int codec_bass_gain; static int audio_mix_modcnt; static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ #if defined(CONFIG_I2S_DLV) int jz_mic_only = 1; static int jz_codec_config = 0; static unsigned long ramp_up_start; static unsigned long ramp_up_end; static unsigned long gain_up_start; static unsigned long gain_up_end; static unsigned long ramp_down_start; static unsigned long ramp_down_end; static unsigned long gain_down_start; static unsigned long gain_down_end; #endif static int codec_mic_gain; static int pop_dma_flag; static int last_dma_buffer_id; static int drain_flag; static int use_mic_line_flag; static void (*old_mksound)(unsigned int hz, unsigned int ticks); extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); static void jz_update_filler(int bits, int channels); static int Init_In_Out_queue(int fragstotal,int fragsize); static int Free_In_Out_queue(int fragstotal,int fragsize); static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); static void (*replay_filler)(signed long src_start, int count, int id); static int (*record_filler)(unsigned long dst_start, int count, int id); #if defined(CONFIG_I2S_ICODEC) static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); #endif static void jz_audio_reset(void); static struct file_operations jz_i2s_audio_fops; static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); struct jz_i2s_controller_info { int io_base; int dma1; /* for play */ int dma2; /* for record */ char *name; int dev_audio; struct i2s_codec *i2s_codec[NR_I2S]; int opened1; int opened2; unsigned char *tmp1; /* tmp buffer for sample conversions */ unsigned char *tmp2; spinlock_t lock; spinlock_t ioctllock; wait_queue_head_t dac_wait; wait_queue_head_t adc_wait; int nextIn; /* byte index to next-in to DMA buffer */ int nextOut; /* byte index to next-out from DMA buffer */ int count; /* current byte count in DMA buffer */ int finish; /* current transfered byte count in DMA buffer */ unsigned total_bytes; /* total bytes written or read */ unsigned blocks; unsigned error; /* over/underrun */ #ifdef CONFIG_PM struct pm_dev *pm; #endif }; static struct jz_i2s_controller_info *i2s_controller = NULL; struct i2s_codec { /* I2S controller connected with */ void *private_data; char *name; int id; int dev_mixer; /* controller specific lower leverl i2s accessing routines */ u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ void (*codec_write) (u8 reg, u16 val); /* Wait for codec-ready */ void (*codec_wait) (struct i2s_codec *codec); /* OSS mixer masks */ int modcnt; int supported_mixers; int stereo_mixers; int record_sources; int bit_resolution; /* OSS mixer interface */ int (*read_mixer) (struct i2s_codec *codec, int oss_channel); void (*write_mixer)(struct i2s_codec *codec, int oss_channel, unsigned int left, unsigned int right); int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); /* saved OSS mixer states */ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; }; typedef struct buffer_queue_s { int count; int *id; int lock; } buffer_queue_t; typedef struct left_right_sample_s { signed long left; signed long right; } left_right_sample_t; static unsigned long pop_turn_onoff_buf; static unsigned long pop_turn_onoff_pbuf; static unsigned long *out_dma_buf = NULL; static unsigned long *out_dma_pbuf = NULL; static unsigned long *out_dma_buf_data_count = NULL; static unsigned long *in_dma_buf = NULL; static unsigned long *in_dma_pbuf = NULL; static unsigned long *in_dma_buf_data_count = NULL; static buffer_queue_t out_empty_queue; static buffer_queue_t out_full_queue; static buffer_queue_t out_busy_queue; static buffer_queue_t in_empty_queue; static buffer_queue_t in_full_queue; static buffer_queue_t in_busy_queue; static int first_record_call = 0; static left_right_sample_t save_last_samples[64]; static inline int get_buffer_id(struct buffer_queue_s *q) { int r; unsigned long flags; int i; spin_lock_irqsave(&q->lock, flags); if (q->count == 0) { spin_unlock_irqrestore(&q->lock, flags); return -1; } r = *(q->id + 0); for (i=0;i < q->count-1;i++) *(q->id + i) = *(q->id + (i+1)); q->count --; spin_unlock_irqrestore(&q->lock, flags); return r; } static inline void put_buffer_id(struct buffer_queue_s *q, int id) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); *(q->id + q->count) = id; q->count ++; spin_unlock_irqrestore(&q->lock, flags); } static inline int elements_in_queue(struct buffer_queue_s *q) { int r; unsigned long flags; spin_lock_irqsave(&q->lock, flags); r = q->count; spin_unlock_irqrestore(&q->lock, flags); return r; } static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) { unsigned long flags; struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; spin_lock_irqsave(&controller->ioctllock, flags); jz_audio_dma_tran_count = count / jz_audio_b; spin_unlock_irqrestore(&controller->ioctllock, flags); flags = claim_dma_lock(); disable_dma(chan); clear_dma_ff(chan); #if 1 jz_set_oss_dma(chan, mode, jz_audio_format); #else set_dma_mode(chan, mode); #endif set_dma_addr(chan, phyaddr); if (count == 0) { count++; printk("JzSOC DMA controller can't set dma 0 count!\n"); } set_dma_count(chan, count); enable_dma(chan); release_dma_lock(flags); } static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) { int id1, id2; unsigned long flags; struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; int dma = controller->dma2; disable_dma(dma); if (__dmac_channel_address_error_detected(dma)) { printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); __dmac_channel_clear_address_error(dma); } if (__dmac_channel_transmit_end_detected(dma)) { __dmac_channel_clear_transmit_end(dma); if(drain_flag == 1) wake_up(&drain_wait_queue); /* for DSP_GETIPTR */ spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes += jz_audio_dma_tran_count; controller->blocks ++; spin_unlock_irqrestore(&controller->ioctllock, flags); id1 = get_buffer_id(&in_busy_queue); put_buffer_id(&in_full_queue, id1); wake_up(&rx_wait_queue); wake_up(&controller->adc_wait); if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_busy_queue, id2); *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); audio_start_dma(dma,dev_id, *(in_dma_pbuf + id2), *(in_dma_buf_data_count + id2), DMA_MODE_READ); } else in_busy_queue.count = 0; } return IRQ_HANDLED; } static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) { int id; unsigned long flags; struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; int dma = controller->dma1; disable_dma(dma); if (__dmac_channel_address_error_detected(dma)) { printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); __dmac_channel_clear_address_error(dma); } if (__dmac_channel_transmit_end_detected(dma)) { __dmac_channel_clear_transmit_end(dma); if(pop_dma_flag == 1) { pop_dma_flag = 0; wake_up(&pop_wait_queue); } else { if(drain_flag == 1) { /* Is replay dma buffer over ? */ if(elements_in_queue(&out_full_queue) <= 0) { drain_flag = 0; wake_up(&drain_wait_queue); } } /* for DSP_GETOPTR */ spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes += jz_audio_dma_tran_count; controller->blocks ++; spin_unlock_irqrestore(&controller->ioctllock, flags); if ((id = get_buffer_id(&out_busy_queue)) < 0) printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); put_buffer_id(&out_empty_queue, id); if ((id = get_buffer_id(&out_full_queue)) >= 0) { put_buffer_id(&out_busy_queue, id); if(*(out_dma_buf_data_count + id) > 0) { audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), *(out_dma_buf_data_count + id), DMA_MODE_WRITE); last_dma_buffer_id = id; } } else out_busy_queue.count = 0; if (elements_in_queue(&out_empty_queue) > 0) { wake_up(&tx_wait_queue); wake_up(&controller->dac_wait); } } } return IRQ_HANDLED; } static void jz_i2s_initHw(int set) { #if defined(CONFIG_MIPS_JZ_URANUS) i2s_clk = 48000000; #else i2s_clk = __cpm_get_i2sclk(); #endif __i2s_disable(); if(set) __i2s_reset(); schedule_timeout(5); if(each_time_init_codec) each_time_init_codec(); __i2s_disable_record(); __i2s_disable_replay(); __i2s_disable_loopback(); __i2s_set_transmit_trigger(4); __i2s_set_receive_trigger(3); } static int Init_In_Out_queue(int fragstotal,int fragsize) { int i; /* recording */ in_empty_queue.count = fragstotal; in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!in_dma_buf) goto all_mem_err; in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!in_dma_pbuf) goto all_mem_err; in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!in_dma_buf_data_count) goto all_mem_err; in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!in_empty_queue.id) goto all_mem_err; in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!in_full_queue.id) goto all_mem_err; in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!in_busy_queue.id) goto all_mem_err; for (i=0;i < fragstotal;i++) *(in_empty_queue.id + i) = i; in_full_queue.count = 0; in_busy_queue.count = 0; for (i = 0; i < fragstotal; i++) { *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); if (*(in_dma_buf + i) == 0) goto mem_failed_in; *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); dma_cache_wback_inv(*(in_dma_buf + i), fragsize); } /* playing */ out_empty_queue.count = fragstotal; out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!out_dma_buf) goto all_mem_err; out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!out_dma_pbuf) goto all_mem_err; out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); if (!out_dma_buf_data_count) goto all_mem_err; out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!out_empty_queue.id) goto all_mem_err; out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!out_full_queue.id) goto all_mem_err; out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); if (!out_busy_queue.id) goto all_mem_err; for (i=0;i < fragstotal;i++) *(out_empty_queue.id + i) = i; out_busy_queue.count = 0; out_full_queue.count = 0; /* alloc DMA buffer */ for (i = 0; i < fragstotal; i++) { *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); if (*(out_dma_buf + i) == 0) { printk(" can't allocate required DMA(OUT) buffers.\n"); goto mem_failed_out; } *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); } return 1; all_mem_err: printk("error:allocate memory occur error 1!\n"); return 0; mem_failed_out: printk("error:allocate memory occur error 2!\n"); for (i = 0; i < fragstotal; i++) { if(*(out_dma_buf + i)) free_pages(*(out_dma_buf + i), get_order(fragsize)); } return 0; mem_failed_in: printk("error:allocate memory occur error 3!\n"); for (i = 0; i < fragstotal; i++) { if(*(in_dma_buf + i)) free_pages(*(in_dma_buf + i), get_order(fragsize)); } return 0; } static int Free_In_Out_queue(int fragstotal,int fragsize) { int i; /* playing */ if(out_dma_buf != NULL) { for (i = 0; i < fragstotal; i++) { if(*(out_dma_buf + i)) free_pages(*(out_dma_buf + i), get_order(fragsize)); *(out_dma_buf + i) = 0; } kfree(out_dma_buf); out_dma_buf = NULL; } if(out_dma_pbuf) { kfree(out_dma_pbuf); out_dma_pbuf = NULL; } if(out_dma_buf_data_count) { kfree(out_dma_buf_data_count); out_dma_buf_data_count = NULL; } if(out_empty_queue.id) { kfree(out_empty_queue.id); out_empty_queue.id = NULL; } if(out_full_queue.id) { kfree(out_full_queue.id); out_full_queue.id = NULL; } if(out_busy_queue.id) { kfree(out_busy_queue.id); out_busy_queue.id = NULL; } out_empty_queue.count = fragstotal; out_busy_queue.count = 0; out_full_queue.count = 0; /* recording */ if(in_dma_buf) { for (i = 0; i < fragstotal; i++) { if(*(in_dma_buf + i)) { dma_cache_wback_inv(*(in_dma_buf + i), fragsize); free_pages(*(in_dma_buf + i), get_order(fragsize)); } *(in_dma_buf + i) = 0; } kfree(in_dma_buf); in_dma_buf = NULL; } if(in_dma_pbuf) { kfree(in_dma_pbuf); in_dma_pbuf = NULL; } if(in_dma_buf_data_count) { kfree(in_dma_buf_data_count); in_dma_buf_data_count = NULL; } if(in_empty_queue.id) { kfree(in_empty_queue.id); in_empty_queue.id = NULL; } if(in_full_queue.id) { kfree(in_full_queue.id); in_full_queue.id = NULL; } if(in_busy_queue.id) { kfree(in_busy_queue.id); in_busy_queue.id = NULL; } in_empty_queue.count = fragstotal; in_full_queue.count = 0; in_busy_queue.count = 0; return 1; } static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) { jz_i2s_initHw(0); } static int jz_audio_set_speed(int dev, int rate) { /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ jz_audio_speed = rate; #if defined(CONFIG_I2S_DLV) if (rate > 96000) rate = 96000; #else if (rate > 48000) rate = 48000; #endif if (rate < 8000) rate = 8000; jz_audio_rate = rate; if(set_codec_speed) set_codec_speed(rate); return jz_audio_rate; } static int record_fill_1x8_u(unsigned long dst_start, int count, int id) { int cnt = 0; unsigned long data; volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); volatile unsigned char *dp = (unsigned char*)dst_start; while (count > 0) { count -= 2; /* count in dword */ cnt++; data = *(s++); *(dp ++) = ((data << 16) >> 24) + 0x80; s++; /* skip the other channel */ } return cnt; } static int record_fill_2x8_u(unsigned long dst_start, int count, int id) { int cnt = 0; unsigned long d1, d2; volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); volatile unsigned char *dp = (unsigned char*)dst_start; while (count > 0) { count -= 2; cnt += 2; d1 = *(s++); *(dp ++) = ((d1 << 16) >> 24) + 0x80; d2 = *(s++); *(dp ++) = ((d2 << 16) >> 24) + 0x80; } return cnt; } static int record_fill_1x16_s(unsigned long dst_start, int count, int id) { int cnt = 0; unsigned long d1; unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); unsigned short *dp = (unsigned short *)dst_start; while (count > 0) { count -= 2; /* count in dword */ cnt += 2; /* count in byte */ d1 = *(s++); *(dp ++) = (d1 << 16) >> 16; s++; /* skip the other channel */ } return cnt; } static int record_fill_2x16_s(unsigned long dst_start, int count, int id) { int cnt = 0; unsigned long d1, d2; unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); unsigned short *dp = (unsigned short *)dst_start; while (count > 0) { count -= 2; /* count in dword */ cnt += 4; /* count in byte */ d1 = *(s++); d2 = *(s++); if(abnormal_data_count > 0) { d1 = d2 = 0; abnormal_data_count --; } *(dp ++) = (d1 << 16) >> 16; *(dp ++) = (d2 << 16) >> 16; } return cnt; } static void replay_fill_1x8_u(signed long src_start, int count, int id) { int cnt = 0; unsigned char data; unsigned long ddata; volatile unsigned char *s = (unsigned char *)src_start; volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); while (count > 0) { count--; cnt += 1; data = *(s++) - 0x80; ddata = (unsigned long) data << 8; *(dp ++) = ddata; *(dp ++) = ddata; /* save last left and right */ if(count == 1) { save_last_samples[id].left = ddata; save_last_samples[id].right = ddata; } } cnt = cnt * 2 * jz_audio_b; *(out_dma_buf_data_count + id) = cnt; } static void replay_fill_2x8_u(signed long src_start, int count, int id) { int cnt = 0; unsigned char d1; unsigned long dd1; volatile unsigned char *s = (unsigned char *)src_start; volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); while (count > 0) { count -= 1; cnt += 1 ; d1 = *(s++) - 0x80; dd1 = (unsigned long) d1 << 8; *(dp ++) = dd1; /* save last left */ if(count == 2) save_last_samples[id].left = dd1; /* save last right */ if(count == 1) save_last_samples[id].right = dd1; } cnt *= jz_audio_b; *(out_dma_buf_data_count + id) = cnt; } static void replay_fill_1x16_s(signed long src_start, int count, int id) { int cnt = 0; signed short d1; signed long l1; volatile signed short *s = (signed short *)src_start; volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); while (count > 0) { count -= 2; cnt += 2 ; d1 = *(s++); l1 = (signed long)d1; #if defined(CONFIG_I2S_ICODEC) l1 >>= codec_volue_shift; #endif *(dp ++) = l1; *(dp ++) = l1; /* save last left and right */ if(count == 1) { save_last_samples[id].left = l1; save_last_samples[id].right = l1; } } cnt = cnt * 2 * jz_audio_b; *(out_dma_buf_data_count + id) = cnt; } static void replay_fill_2x16_s(signed long src_start, int count, int id) { int cnt = 0; signed short d1; signed long l1; volatile signed short *s = (signed short *)src_start; volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); #if defined(CONFIG_I2S_ICODEC) int mute_cnt = 0; signed long tmp1,tmp2; volatile signed long *before_dp; int sam_rate = jz_audio_rate / 20; tmp1 = tmp2 = 0; while (count > 0) { count -= 2; cnt += 2; d1 = *(s++); l1 = (signed long)d1; l1 >>= codec_volue_shift; if(l1 == 0) { mute_cnt ++; if(mute_cnt >= sam_rate) { before_dp = dp - 10; *(before_dp) = (signed long)1; before_dp = dp - 11; *(before_dp) = (signed long)1; mute_cnt = 0; } } else mute_cnt = 0; *(dp ++) = l1; tmp1 = tmp2; tmp2 = l1; } /* save last left */ save_last_samples[id].left = tmp1; /* save last right */ save_last_samples[id].right = tmp2; #endif #if defined(CONFIG_I2S_DLV) while (count > 0) { count -= 2; cnt += 2; d1 = *(s++); l1 = (signed long)d1; *(dp ++) = l1; } #endif cnt *= jz_audio_b; *(out_dma_buf_data_count + id) = cnt; } static void replay_fill_2x18_s(unsigned long src_start, int count, int id) { int cnt = 0; signed long d1; signed long l1; volatile signed long *s = (signed long *)src_start; volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); while (count > 0) { count -= 4; cnt += 4; d1 = *(s++); l1 = (signed long)d1; *(dp ++) = l1; } cnt *= jz_audio_b; *(out_dma_buf_data_count + id) = cnt; } static unsigned int jz_audio_set_format(int dev, unsigned int fmt) { switch (fmt) { case AFMT_U8: __i2s_set_oss_sample_size(8); __i2s_set_iss_sample_size(8); jz_audio_format = fmt; jz_update_filler(jz_audio_format,jz_audio_channels); break; case AFMT_S16_LE: #if defined(CONFIG_I2S_DLV) /* DAC path and ADC path */ write_codec_file(2, 0x00); //write_codec_file(2, 0x60); #endif jz_audio_format = fmt; jz_update_filler(jz_audio_format,jz_audio_channels); __i2s_set_oss_sample_size(16); __i2s_set_iss_sample_size(16); break; case 18: __i2s_set_oss_sample_size(18); jz_audio_format = fmt; jz_update_filler(jz_audio_format,jz_audio_channels); break; case AFMT_QUERY: break; } return jz_audio_format; } static short jz_audio_set_channels(int dev, short channels) { switch (channels) { case 1: if(set_codec_some_func) set_codec_some_func(); jz_audio_channels = channels; jz_update_filler(jz_audio_format, jz_audio_channels); #if defined(CONFIG_I2S_DLV) write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono #endif break; case 2: jz_audio_channels = channels; jz_update_filler(jz_audio_format, jz_audio_channels); #if defined(CONFIG_I2S_DLV) write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo #endif break; case 0: break; } return jz_audio_channels; } static void init_codec(void) { /* inititalize internal I2S codec */ if(init_codec_pin) init_codec_pin(); #if defined(CONFIG_I2S_ICDC) /* initialize AIC but not reset it */ jz_i2s_initHw(0); #endif if(reset_codec) reset_codec(); } static void jz_audio_reset(void) { __i2s_disable_replay(); __i2s_disable_receive_dma(); __i2s_disable_record(); __i2s_disable_transmit_dma(); #if defined(CONFIG_I2S_DLV) REG_AIC_I2SCR = 0x10; #endif init_codec(); } static int jz_audio_release(struct inode *inode, struct file *file); static int jz_audio_open(struct inode *inode, struct file *file); static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); /* static struct file_operations jz_i2s_audio_fops */ static struct file_operations jz_i2s_audio_fops = { owner: THIS_MODULE, open: jz_audio_open, release: jz_audio_release, write: jz_audio_write, read: jz_audio_read, poll: jz_audio_poll, ioctl: jz_audio_ioctl }; static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) { int i; int minor = MINOR(inode->i_rdev); struct jz_i2s_controller_info *controller = i2s_controller; for (i = 0; i < NR_I2S; i++) if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) goto match; if (!controller) return -ENODEV; match: file->private_data = controller->i2s_codec[i]; return 0; } static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct i2s_codec *codec = (struct i2s_codec *)file->private_data; return codec->mixer_ioctl(codec, cmd, arg); } static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } static struct file_operations jz_i2s_mixer_fops = { owner: THIS_MODULE, llseek: jz_i2s_llseek, ioctl: jz_i2s_ioctl_mixdev, open: jz_i2s_open_mixdev, }; static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) { int ret; long val = 0; switch (cmd) { case SOUND_MIXER_INFO: if(codec_mixer_info_id_name) codec_mixer_info_id_name(); info.modify_counter = audio_mix_modcnt; return copy_to_user((void *)arg, &info, sizeof(info)); case SOUND_OLD_MIXER_INFO: if(codec_mixer_old_info_id_name) codec_mixer_old_info_id_name(); return copy_to_user((void *)arg, &old_info, sizeof(info)); case SOUND_MIXER_READ_STEREODEVS: return put_user(0, (long *) arg); case SOUND_MIXER_READ_CAPS: val = SOUND_CAP_EXCL_INPUT; return put_user(val, (long *) arg); case SOUND_MIXER_READ_DEVMASK: break; case SOUND_MIXER_READ_RECMASK: break; case SOUND_MIXER_READ_RECSRC: break; case SOUND_MIXER_WRITE_SPEAKER: ret = get_user(val, (long *) arg); if (ret) return ret; val = val & 0xff; if(val < 0) val = 0; if(val > 100) val = 100; switch(val) { case 100: if(set_codec_direct_mode) set_codec_direct_mode(); break; case 0: if(clear_codec_direct_mode) clear_codec_direct_mode(); break; } break; case SOUND_MIXER_WRITE_BASS: ret = get_user(val, (long *) arg); if (ret) return ret; val = val & 0xff; if(val < 0) val = 0; if(val > 100) val = 100; codec_bass_gain = val; if(set_codec_bass) set_codec_bass(val); return 0; case SOUND_MIXER_READ_BASS: val = codec_bass_gain; ret = val << 8; val = val | ret; return put_user(val, (long *) arg); case SOUND_MIXER_WRITE_VOLUME: ret = get_user(val, (long *) arg); if (ret) return ret; val = val & 0xff; if(val < 0) val = 0; if(val > 100) val = 100; jz_audio_volume = val; if(set_codec_volume) set_codec_volume(val); return 0; case SOUND_MIXER_READ_VOLUME: val = jz_audio_volume; ret = val << 8; val = val | ret; return put_user(val, (long *) arg); case SOUND_MIXER_WRITE_MIC: ret = get_user(val, (long *) arg); if (ret) return ret; val = val & 0xff; if(val < 0) val = 0; if(val > 100) val = 100; codec_mic_gain = val; use_mic_line_flag = USE_MIC; if(set_codec_mic) set_codec_mic(val); return 0; case SOUND_MIXER_READ_MIC: val = codec_mic_gain; ret = val << 8; val = val | ret; return put_user(val, (long *) arg); case SOUND_MIXER_WRITE_LINE: ret = get_user(val, (long *) arg); if (ret) return ret; val = val & 0xff; if(val < 0) val = 0; if(val > 100) val = 100; use_mic_line_flag = USE_LINEIN; codec_mic_gain = val; if(set_codec_line) set_codec_line(val); return 0; case SOUND_MIXER_READ_LINE: val = codec_mic_gain; ret = val << 8; val = val | ret; return put_user(val, (long *) arg); default: return -ENOSYS; } audio_mix_modcnt ++; return 0; } int i2s_probe_codec(struct i2s_codec *codec) { /* generic OSS to I2S wrapper */ codec->mixer_ioctl = i2s_mixer_ioctl; return 1; } /* I2S codec initialisation. */ static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) { int num_i2s = 0; struct i2s_codec *codec; for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) return -ENOMEM; memset(codec, 0, sizeof(struct i2s_codec)); codec->private_data = controller; codec->id = num_i2s; if (i2s_probe_codec(codec) == 0) break; if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); kfree(codec); break; } controller->i2s_codec[num_i2s] = codec; } return num_i2s; } static void jz_update_filler(int format, int channels) { #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) switch (TYPE(format, channels)) { case TYPE(AFMT_U8, 1): jz_audio_b = 4; /* 4bytes * 8bits =32bits */ replay_filler = replay_fill_1x8_u; record_filler = record_fill_1x8_u; break; case TYPE(AFMT_U8, 2): jz_audio_b = 4; replay_filler = replay_fill_2x8_u; record_filler = record_fill_2x8_u; break; case TYPE(AFMT_S16_LE, 1): jz_audio_b = 2; /* 2bytes * 16bits =32bits */ replay_filler = replay_fill_1x16_s; record_filler = record_fill_1x16_s; break; case TYPE(AFMT_S16_LE, 2): jz_audio_b = 2; replay_filler = replay_fill_2x16_s; record_filler = record_fill_2x16_s; break; case TYPE(18, 2): jz_audio_b = 1; replay_filler = replay_fill_2x18_s; record_filler = record_fill_2x16_s; break; default: ; } } #ifdef CONFIG_PROC_FS extern struct proc_dir_entry *proc_jz_root; int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { return 0; } static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) { if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) return -EIO; return 0; } static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) { } #endif static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) { char *name; int adev; /* No of Audio device. */ name = controller->name; /* initialize AIC controller and reset it */ jz_i2s_initHw(1); adev = register_sound_dsp(&jz_i2s_audio_fops, -1); if (adev < 0) goto audio_failed; /* initialize I2S codec and register /dev/mixer */ if (jz_i2s_codec_init(controller) <= 0) goto mixer_failed; #ifdef CONFIG_PROC_FS if (jz_i2s_init_proc(controller) < 0) { printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); goto proc_failed; } #endif controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); if (!controller->tmp1) { printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); goto tmp1_failed; } controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); if (!controller->tmp2) { printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); goto tmp2_failed; } if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); goto dma2_failed; } if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); goto dma1_failed; } printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); controller->dev_audio = adev; pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); if(!pop_turn_onoff_buf) printk("pop_turn_onoff_buf alloc is wrong!\n"); pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); return; dma2_failed: jz_free_dma(controller->dma1); dma1_failed: free_pages((unsigned long)controller->tmp2, 8); tmp2_failed: free_pages((unsigned long)controller->tmp1, 8); tmp1_failed: #ifdef CONFIG_PROC_FS jz_i2s_cleanup_proc(controller); #endif proc_failed: /* unregister mixer dev */ mixer_failed: unregister_sound_dsp(adev); audio_failed: return; } static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) { if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), GFP_KERNEL)) == NULL) { printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); return -ENOMEM; } (*controller)->name = "Jz I2S controller"; (*controller)->opened1 = 0; (*controller)->opened2 = 0; init_waitqueue_head(&(*controller)->adc_wait); init_waitqueue_head(&(*controller)->dac_wait); spin_lock_init(&(*controller)->lock); init_waitqueue_head(&rx_wait_queue); init_waitqueue_head(&tx_wait_queue); init_waitqueue_head(&pop_wait_queue); init_waitqueue_head(&drain_wait_queue); return 0; } static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) { int adev = controller->dev_audio; jz_i2s_full_reset(controller); controller->dev_audio = -1; if (old_mksound) kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ #ifdef CONFIG_PROC_FS jz_i2s_cleanup_proc(controller); #endif jz_free_dma(controller->dma1); jz_free_dma(controller->dma2); free_pages((unsigned long)controller->tmp1, 8); free_pages((unsigned long)controller->tmp2, 8); free_pages((unsigned long)pop_turn_onoff_buf, 8); if (adev >= 0) { /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ unregister_sound_dsp(controller->dev_audio); } } #ifdef CONFIG_PM static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) { if(i2s_suspend_codec) i2s_suspend_codec(controller->opened1,controller->opened2); printk("Aic and codec are suspended!\n"); return 0; } static int jz_i2s_resume(struct jz_i2s_controller_info *controller) { if(i2s_resume_codec) i2s_resume_codec(); #if defined(CONFIG_I2S_AK4642EN) jz_i2s_initHw(0); jz_audio_reset(); __i2s_enable(); jz_audio_set_speed(controller->dev_audio,jz_audio_speed); /* playing */ if(controller->opened1) { if(set_codec_replay) set_codec_replay(); int dma = controller->dma1; int id; unsigned long flags; disable_dma(dma); if(__dmac_channel_address_error_detected(dma)) { printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); __dmac_channel_clear_address_error(dma); } if(__dmac_channel_transmit_end_detected(dma)) __dmac_channel_clear_transmit_end(dma); /* for DSP_GETOPTR */ spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes += jz_audio_dma_tran_count; controller->blocks ++; spin_unlock_irqrestore(&controller->ioctllock, flags); while((id = get_buffer_id(&out_busy_queue)) >= 0) put_buffer_id(&out_empty_queue, id); out_busy_queue.count=0; if((id = get_buffer_id(&out_full_queue)) >= 0) { put_buffer_id(&out_empty_queue, id); } if (elements_in_queue(&out_empty_queue) > 0) { wake_up(&tx_wait_queue); wake_up(&controller->dac_wait); } else printk("pm out_empty_queue empty"); } /* recording */ if(controller->opened2) { if(set_codec_record) set_codec_record(use_mic_line_flag); int dma = controller->dma2; int id1, id2; unsigned long flags; disable_dma(dma); if (__dmac_channel_address_error_detected(dma)) { printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); __dmac_channel_clear_address_error(dma); } if (__dmac_channel_transmit_end_detected(dma)) { __dmac_channel_clear_transmit_end(dma); } /* for DSP_GETIPTR */ spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes += jz_audio_dma_tran_count; controller->blocks ++; spin_unlock_irqrestore(&controller->ioctllock, flags); id1 = get_buffer_id(&in_busy_queue); put_buffer_id(&in_full_queue, id1); wake_up(&rx_wait_queue); wake_up(&controller->adc_wait); if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_full_queue, id2); } in_busy_queue.count = 0; } #endif return 0; } static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) { int ret; struct jz_i2s_controller_info *controller = pm_dev->data; if (!controller) return -EINVAL; switch (req) { case PM_SUSPEND: ret = jz_i2s_suspend(controller, (int)data); break; case PM_RESUME: ret = jz_i2s_resume(controller); break; default: ret = -EINVAL; break; } return ret; } #endif /* CONFIG_PM */ #if defined(CONFIG_I2S_DLV) static irqreturn_t aic_codec_irq(int irq, void *dev_id) { u8 file_9 = read_codec_file(9); u8 file_8 = read_codec_file(8); //printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9); if ((file_9 & 0x1f) == 0x10) { write_codec_file(8, 0x3f); write_codec_file_bit(5, 1, 6);//SB_OUT->1 mdelay(300); while ((read_codec_file(9) & 0x4) != 0x4); while ((read_codec_file(9) & 0x10) == 0x10) { write_codec_file(9, 0x10); } write_codec_file_bit(5, 0, 6);//SB_OUT->0 mdelay(300); while ((read_codec_file(9) & 0x8) != 0x8); write_codec_file(9, file_9); write_codec_file(8, file_8); return IRQ_HANDLED; } if (file_9 & 0x8) ramp_up_end = jiffies; else if (file_9 & 0x4) ramp_down_end = jiffies; else if (file_9 & 0x2) gain_up_end = jiffies; else if (file_9 & 0x1) gain_down_end = jiffies; write_codec_file(9, file_9); if (file_9 & 0xf) wake_up(&pop_wait_queue); while (REG_ICDC_RGDATA & 0x100); return IRQ_HANDLED; } #endif static int __init init_jz_i2s(void) { int errno; #if defined(CONFIG_I2S_DLV) int retval; ramp_up_start = 0; ramp_up_end = 0; gain_up_start = 0; gain_up_end = 0; ramp_down_start = 0; ramp_down_end = 0; gain_down_start = 0; gain_down_end = 0; #endif use_mic_line_flag = USE_NONE; abnormal_data_count = 0; if(set_codec_mode) set_codec_mode(); drain_flag = 0; if ((errno = probe_jz_i2s(&i2s_controller)) < 0) return errno; if(set_codec_gpio_pin) set_codec_gpio_pin(); attach_jz_i2s(i2s_controller); if(set_codec_startup_param) set_codec_startup_param(); if(set_codec_volume_table) set_codec_volume_table(); #if defined(CONFIG_I2S_DLV) jz_codec_config = 0; retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL); if (retval) { printk("Could not get aic codec irq %d\n", IRQ_AIC); return retval; } #endif out_empty_queue.id = NULL; out_full_queue.id = NULL; out_busy_queue.id = NULL; in_empty_queue.id = NULL; in_full_queue.id = NULL; in_busy_queue.id = NULL; jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); #ifdef CONFIG_PM i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, jz_i2s_pm_callback); if (i2s_controller->pm) i2s_controller->pm->data = i2s_controller; #endif #if defined(CONFIG_I2S_DLV) __cpm_start_idct(); __cpm_start_db(); __cpm_start_me(); __cpm_start_mc(); __cpm_start_ipu(); #endif printk("JZ I2S OSS audio driver initialized\n"); return 0; } static void __exit cleanup_jz_i2s(void) { #ifdef CONFIG_PM /* pm_unregister(i2s_controller->pm); */ #endif #if defined(CONFIG_I2S_DLV) free_irq(IRQ_AIC, NULL); #endif unload_jz_i2s(i2s_controller); Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); if(clear_codec_mode) clear_codec_mode(); } module_init(init_jz_i2s); module_exit(cleanup_jz_i2s); #if defined(CONFIG_SOC_JZ4730) static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) { DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count,con; if(elements_in_queue(&in_busy_queue) > 0) { if (nonblock) return -EBUSY; drain_flag = 1; sleep_on(&drain_wait_queue); drain_flag = 0; } else { add_wait_queue(&ctrl->adc_wait, &wait); for (con = 0; con < 1000; con ++) { udelay(1); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&ctrl->lock, flags); count = get_dma_residue(ctrl->dma2); spin_unlock_irqrestore(&ctrl->lock, flags); if (count <= 0) break; if (nonblock) { remove_wait_queue(&ctrl->adc_wait, &wait); current->state = TASK_RUNNING; return -EBUSY; } } remove_wait_queue(&ctrl->adc_wait, &wait); current->state = TASK_RUNNING; } return 0; } static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) { DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count; if(elements_in_queue(&out_full_queue) > 0) { if (nonblock) return -EBUSY; drain_flag = 1; sleep_on(&drain_wait_queue); drain_flag = 0; } else { add_wait_queue(&(ctrl->dac_wait), &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if(elements_in_queue(&out_full_queue) <= 0) { spin_lock_irqsave(&ctrl->lock, flags); count = get_dma_residue(ctrl->dma1); spin_unlock_irqrestore(&ctrl->lock, flags); if(count <= 0) break; } if (nonblock) { remove_wait_queue(&ctrl->dac_wait, &wait); current->state = TASK_RUNNING; return -EBUSY; } } remove_wait_queue(&ctrl->dac_wait, &wait); current->state = TASK_RUNNING; } return 0; } #endif #if defined(CONFIG_SOC_JZ4740) #define MAXDELAY 50000 static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) { int count,ele,i=0; int tfl; for (;;) { if(!nonblock) {//blocked if ( i < MAXDELAY ) { udelay(10); i++; } else break; ele = elements_in_queue(&out_full_queue); if(ele <= 0) { udelay(10); spin_lock(&ctrl->lock); count = get_dma_residue(ctrl->dma1); spin_unlock(&ctrl->lock); if (count <= 0) break; } } else {//non-blocked mdelay(100); ele = elements_in_queue(&out_full_queue); if(ele <= 0) { mdelay(100); spin_lock(&ctrl->lock); count = get_dma_residue(ctrl->dma1); spin_unlock(&ctrl->lock); if (count <= 0) break; } } } /* wait for TX fifo */ while (1) { tfl = __aic_get_transmit_resident(); if (tfl == 0) break; udelay(2); } return 0; } static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) { int count,i=0; for (;;) { if ( i < MAXDELAY ) { udelay(10); i++; } else break; spin_lock(&ctrl->lock); count = get_dma_residue(ctrl->dma2); spin_unlock(&ctrl->lock); if (count <= 0) break; if (nonblock) { return -EBUSY; } } return 0; } #endif #if defined(CONFIG_SOC_JZ4750) #define MAXDELAY 50000 static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) { //DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count,i=0; //add_wait_queue(&ctrl->adc_wait, &wait); for (;;) { if (i < MAXDELAY) { udelay(10); i++; } else break; //set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&ctrl->lock, flags); //spin_lock(&ctrl->lock); count = get_dma_residue(ctrl->dma2); spin_unlock_irqrestore(&ctrl->lock, flags); //spin_unlock(&ctrl->lock); if (count <= 0) break; /*if (signal_pending(current)) break;*/ if (nonblock) { //remove_wait_queue(&ctrl->adc_wait, &wait); //current->state = TASK_RUNNING; return -EBUSY; } } //remove_wait_queue(&ctrl->adc_wait, &wait); //current->state = TASK_RUNNING; /*if (signal_pending(current)) return -ERESTARTSYS;*/ return 0; } static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) { unsigned long flags; int count,ele,busyele,emptyele,i=0; for (;;) { if(!nonblock) {//blocked if (i < MAXDELAY) { udelay(10); i++; } else break; ele = elements_in_queue(&out_full_queue); if(ele <= 0) { udelay(200); busyele = elements_in_queue(&out_busy_queue); emptyele = elements_in_queue(&out_empty_queue); if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { spin_lock_irqsave(&ctrl->lock, flags); count = get_dma_residue(ctrl->dma1); spin_unlock_irqrestore(&ctrl->lock, flags); if (count <= 0) break; } } } else {//non-blocked //mdelay(100); ele = elements_in_queue(&out_full_queue); if(ele <= 0) { //mdelay(100); busyele = elements_in_queue(&out_busy_queue); emptyele = elements_in_queue(&out_empty_queue); if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { spin_lock_irqsave(&ctrl->lock, flags); count = get_dma_residue(ctrl->dma1); spin_unlock_irqrestore(&ctrl->lock, flags); if (count <= 0) break; } } } } return 0; } #endif static int jz_audio_release(struct inode *inode, struct file *file) { unsigned long flags; #if defined(CONFIG_I2S_DLV) unsigned long tfl; #endif struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; #if defined(CONFIG_I2S_DLV) jz_codec_config = 0; #endif if (controller == NULL) return -ENODEV; pop_dma_flag = 0; if (controller->opened1 == 1 && controller->opened2 == 1) { controller->opened1 = 0; __i2s_enable_transmit_dma(); __i2s_enable_replay(); drain_dac(controller, file->f_flags & O_NONBLOCK); #if defined(CONFIG_I2S_DLV) /* wait for fifo empty */ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 gain_down_start = jiffies; sleep_on(&pop_wait_queue); //gain_down_end = jiffies; /*while (1) { tfl = REG_AIC_SR & 0x00003f00; if (tfl == 0) { udelay(500); break; } mdelay(2); }*/ mdelay(100); #endif disable_dma(controller->dma1); set_dma_count(controller->dma1, 0); __i2s_disable_transmit_dma(); __i2s_disable_replay(); #if defined(CONFIG_I2S_ICODEC) if(clear_codec_replay) clear_codec_replay(); #endif spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes = 0; controller->count = 0; controller->finish = 0; jz_audio_dma_tran_count = 0; controller->blocks = 0; controller->nextOut = 0; spin_unlock_irqrestore(&controller->ioctllock, flags); controller->opened2 = 0; first_record_call = 1; __i2s_enable_receive_dma(); __i2s_enable_record(); drain_adc(controller, file->f_flags & O_NONBLOCK); disable_dma(controller->dma2); set_dma_count(controller->dma2, 0); __i2s_disable_receive_dma(); __i2s_disable_record(); #if defined(CONFIG_I2S_ICODEC) if(clear_codec_record) clear_codec_record(); #endif spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextIn = 0; spin_unlock_irqrestore(&controller->ioctllock, flags); #if defined(CONFIG_I2S_DLV) write_codec_file_bit(5, 1, 6);//SB_OUT->1 ramp_down_start = jiffies; sleep_on(&pop_wait_queue); //ramp_down_end = jiffies; if (use_mic_line_flag == USE_LINEIN) { unset_record_line_input_audio_with_audio_data_replay(); //printk("3 use_mic_line_flag=%d\n",use_mic_line_flag); } if (use_mic_line_flag == USE_MIC) { unset_record_mic_input_audio_with_audio_data_replay(); //printk("4 use_mic_line_flag=%d\n",use_mic_line_flag); } #if 0 unset_record_playing_audio_mixed_with_mic_input_audio(); #endif #endif __i2s_disable(); if(turn_off_codec) turn_off_codec(); abnormal_data_count = 0; } else if (controller->opened1 == 1) { //controller->opened1 = 0; __i2s_enable_transmit_dma(); __i2s_enable_replay(); drain_dac(controller, file->f_flags & O_NONBLOCK); /* add some mute to anti-pop */ #if defined(CONFIG_I2S_ICODEC) //write_mute_to_dma_buffer(save_last_samples[last_dma_buffer_id].left,save_last_samples[last_dma_buffer_id].right); #endif #if defined(CONFIG_I2S_DLV) write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 gain_down_start = jiffies; sleep_on(&pop_wait_queue); //gain_down_end = jiffies; while (1) { tfl = REG_AIC_SR & 0x00003f00; if (tfl == 0) { udelay(500); break; } mdelay(2); } #endif disable_dma(controller->dma1); set_dma_count(controller->dma1, 0); __i2s_disable_transmit_dma(); __i2s_disable_replay(); #if defined(CONFIG_I2S_ICODEC) if(clear_codec_replay) clear_codec_replay(); #endif __aic_flush_fifo(); spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes = 0; controller->count = 0; controller->finish = 0; jz_audio_dma_tran_count = 0; controller->blocks = 0; controller->nextOut = 0; spin_unlock_irqrestore(&controller->ioctllock, flags); #if defined(CONFIG_I2S_DLV) write_codec_file_bit(5, 1, 6);//SB_OUT->1 ramp_down_start = jiffies; sleep_on(&pop_wait_queue); //ramp_down_end = jiffies; unset_audio_data_replay(); #endif __i2s_disable(); #if defined(CONFIG_I2S_ICODEC) if(turn_off_codec) turn_off_codec(); #endif } else if (controller->opened2 == 1) { controller->opened2 = 0; first_record_call = 1; __i2s_enable_receive_dma(); __i2s_enable_record(); drain_adc(controller, file->f_flags & O_NONBLOCK); disable_dma(controller->dma2); set_dma_count(controller->dma2, 0); __i2s_disable_receive_dma(); __i2s_disable_record(); #if defined(CONFIG_I2S_ICODEC) if(clear_codec_record) clear_codec_record(); #endif spin_lock_irqsave(&controller->ioctllock, flags); controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextIn = 0; spin_unlock_irqrestore(&controller->ioctllock, flags); #if defined(CONFIG_I2S_DLV) #if 0 /* unset Record MIC input audio with direct playback */ unset_record_mic_input_audio_with_direct_playback(); #endif #if 1 /* unset Record MIC input audio without playback */ unset_record_mic_input_audio_without_playback(); #endif #if 0 /* tested */ /* unset Record LINE input audio without playback */ unset_record_line_input_audio_without_playback(); #endif #endif __i2s_disable(); #if defined(CONFIG_I2S_ICODEC) if(turn_off_codec) turn_off_codec(); #endif abnormal_data_count = 0; } #if defined(CONFIG_I2S_DLV) write_codec_file(9, 0xff); write_codec_file(8, 0x3f); /* __cpm_stop_idct(); __cpm_stop_db(); __cpm_stop_me(); __cpm_stop_mc(); __cpm_stop_ipu();*/ #endif if (controller->opened1 == 1 && controller->opened2 == 1) { controller->opened1 = 0; controller->opened2 = 0; //print_pop_duration(); //__dmac_disable_module(0); } else if ( controller->opened1 == 1 ) { controller->opened1 = 0; //print_pop_duration(); } else if ( controller->opened2 == 1 ) { controller->opened2 = 0; } return 0; } static int jz_audio_open(struct inode *inode, struct file *file) { int i; struct jz_i2s_controller_info *controller = i2s_controller; #if defined(CONFIG_I2S_DLV) jz_codec_config = 0; #endif if (controller == NULL) return -ENODEV; mdelay(2); #if defined(CONFIG_I2S_DLV) REG_DMAC_DMACKE(0) = 0x3f; #endif pop_dma_flag = 0; if (controller->opened1 == 1 || controller->opened2 == 1 ) { printk("\naudio is busy!\n"); return -EBUSY; } if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { if (controller->opened1 == 1) return -EBUSY; controller->opened1 = 1; /* for ioctl */ controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextOut = 0; for(i=0;i < 64;i++) { save_last_samples[i].left = 0; save_last_samples[i].right = 0; } out_empty_queue.count = jz_audio_fragstotal; for (i=0;i < jz_audio_fragstotal;i++) *(out_empty_queue.id + i) = i; out_busy_queue.count = 0; out_full_queue.count = 0; last_dma_buffer_id = 0; if (controller->opened2 == 1) return -EBUSY; controller->opened2 = 1; first_record_call = 1; /* for ioctl */ controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextIn = 0; in_empty_queue.count = jz_audio_fragstotal; for (i=0;i < jz_audio_fragstotal;i++) *(in_empty_queue.id + i) = i; in_full_queue.count = 0; in_busy_queue.count = 0; } else if (file->f_mode & FMODE_WRITE) { if (controller->opened1 == 1) return -EBUSY; controller->opened1 = 1; /* for ioctl */ controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextOut = 0; for(i=0;i < 64;i++) { save_last_samples[i].left = 0; save_last_samples[i].right = 0; } out_empty_queue.count = jz_audio_fragstotal; for (i=0;i < jz_audio_fragstotal;i++) *(out_empty_queue.id + i) = i; out_busy_queue.count = 0; out_full_queue.count = 0; last_dma_buffer_id = 0; } else if (file->f_mode & FMODE_READ) { if (controller->opened2 == 1) return -EBUSY; controller->opened2 = 1; first_record_call = 1; /* for ioctl */ controller->total_bytes = 0; jz_audio_dma_tran_count = 0; controller->count = 0; controller->finish = 0; controller->blocks = 0; controller->nextIn = 0; in_empty_queue.count = jz_audio_fragstotal; for (i=0;i < jz_audio_fragstotal;i++) *(in_empty_queue.id + i) = i; in_full_queue.count = 0; in_busy_queue.count = 0; } file->private_data = controller; jz_audio_reset(); REG_AIC_FR |= (1 << 6); if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { #if defined(CONFIG_I2S_ICODEC) if (set_codec_replay_record) set_codec_replay_record(use_mic_line_flag); #endif #if defined(CONFIG_I2S_DLV) if (use_mic_line_flag == USE_NONE) { printk("you select mic or line recording please.or use mic recording!\n"); use_mic_line_flag = USE_MIC; } if (use_mic_line_flag == USE_LINEIN) { /* Record LINE input audio with Audio data replay (full duplex for linein) */ /* codec_test_line */ set_record_line_input_audio_with_audio_data_replay(); } if (use_mic_line_flag == USE_MIC) { /* Record MIC input audio with Audio data replay (full duplex) */ /* codec_test_mic */ set_record_mic_input_audio_with_audio_data_replay(); } #if 0 /* Record playing audio mixed with MIC input audio */ set_record_playing_audio_mixed_with_mic_input_audio(); #endif #endif } else if (file->f_mode & FMODE_WRITE) { #if defined(CONFIG_I2S_ICODEC) if(set_codec_replay) set_codec_replay(); #endif #if defined(CONFIG_I2S_DLV) //mdelay(10); /* Audio data replay */ set_audio_data_replay(); #endif } else if (file->f_mode & FMODE_READ) { #if defined(CONFIG_I2S_ICODEC) abnormal_data_count = 0; if(set_codec_record) set_codec_record(use_mic_line_flag); #endif #if defined(CONFIG_I2S_DLV) #if 0 /* Record MIC input audio with direct playback */ set_record_mic_input_audio_with_direct_playback(); #endif #if 1 /* set Record MIC input audio without playback */ set_record_mic_input_audio_without_playback(); #endif #if 0 /* tested */ /* set Record LINE input audio without playback */ set_record_line_input_audio_without_playback(); #endif mdelay(1); #endif } #if defined(CONFIG_I2S_DLV) __aic_reset(); mdelay(10); REG_AIC_I2SCR = 0x10; mdelay(20); __aic_flush_fifo(); #endif __i2s_enable(); #if defined(CONFIG_I2S_DLV) ndelay(100); if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { #if defined(CONFIG_I2S_DLV) //set SB_ADC or SB_DAC __dmac_enable_module(0); write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ramp_up_start = jiffies; sleep_on(&pop_wait_queue); //ramp_up_end = jiffies; #endif } else if (file->f_mode & FMODE_WRITE) { #if defined(CONFIG_I2S_DLV) write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ramp_up_start = jiffies; /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); REG_RTC_RCR = 0x1; while (!(REG_RTC_RCR & RTC_RCR_WRDY)); REG_RTC_RGR = 1;*/ sleep_on(&pop_wait_queue); //ramp_up_end = jiffies; write_codec_file_bit(5, 1, 4);//SB_ADC->1 #endif } else if (file->f_mode & FMODE_READ) { #if defined(CONFIG_I2S_DLV) if (jz_mic_only) write_codec_file_bit(5, 1, 7);//SB_DAC->1 else write_codec_file_bit(5, 0, 7);//SB_DAC->0 mdelay(500); #endif } #endif return 0; } static int jz_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int val,fullc,busyc,unfinish,newfragstotal,newfragsize; struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; count_info cinfo; audio_buf_info abinfo; int id, i; val = 0; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_RESET: #if 0 jz_audio_reset(); __i2s_disable_replay(); __i2s_disable_receive_dma(); __i2s_disable_record(); __i2s_disable_transmit_dma(); #endif return 0; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) return drain_dac(controller, file->f_flags & O_NONBLOCK); return 0; case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, (int *)arg)) return -EFAULT; if (val >= 0) jz_audio_set_speed(controller->dev_audio, val); return put_user(val, (int *)arg); case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, (int *)arg)) return -EFAULT; jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); return 0; case SNDCTL_DSP_GETBLKSIZE: //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); return put_user(jz_audio_fragsize, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); case SNDCTL_DSP_SETFMT: /* Select sample format */ if (get_user(val, (int *)arg)) return -EFAULT; if (val != AFMT_QUERY) jz_audio_set_format(controller->dev_audio,val); else { if (file->f_mode & FMODE_READ) val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; else val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; } return put_user(val, (int *)arg); case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; jz_audio_set_channels(controller->dev_audio, val); return put_user(val, (int *)arg); case SNDCTL_DSP_POST: /* FIXME: the same as RESET ?? */ return 0; case SNDCTL_DSP_SUBDIVIDE: return 0; case SNDCTL_DSP_SETFRAGMENT: get_user(val, (long *) arg); newfragsize = 1 << (val & 0xFFFF); if (newfragsize < 4 * PAGE_SIZE) newfragsize = 4 * PAGE_SIZE; if (newfragsize > (16 * PAGE_SIZE)) newfragsize = 16 * PAGE_SIZE; newfragstotal = (val >> 16) & 0x7FFF; if (newfragstotal < 2) newfragstotal = 2; if (newfragstotal > 32) newfragstotal = 32; if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) return 0; Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); mdelay(500); jz_audio_fragstotal = newfragstotal; jz_audio_fragsize = newfragsize; Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); mdelay(10); return 0; case SNDCTL_DSP_GETCAPS: return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_SETDUPLEX: return -EINVAL; case SNDCTL_DSP_GETOSPACE: { int i; unsigned long bytes = 0; if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); jz_audio_fragments = elements_in_queue(&out_empty_queue); for (i = 0; i < jz_audio_fragments; i++) bytes += jz_audio_fragsize; if (jz_audio_channels == 2) bytes /= jz_audio_b; else if (jz_audio_channels == 1) bytes /= 4; else printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); /* unused fragment amount */ abinfo.fragments = jz_audio_fragments; /* amount of fragments */ abinfo.fragstotal = jz_audio_fragstotal; /* fragment size in bytes */ if (jz_audio_channels == 2) abinfo.fragsize = jz_audio_fragsize / jz_audio_b; else if (jz_audio_channels == 1) abinfo.fragsize = jz_audio_fragsize / 4; else printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); /* write size count without blocking in bytes */ abinfo.bytes = (int)bytes; return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; } case SNDCTL_DSP_GETISPACE: { int i; unsigned long bytes = 0; if (!(file->f_mode & FMODE_READ)) return -EINVAL; jz_audio_fragments = elements_in_queue(&in_empty_queue); for (i = 0; i < jz_audio_fragments; i++) bytes += jz_audio_fragsize; if (jz_audio_channels == 2) bytes /= jz_audio_b; else if (jz_audio_channels == 1) bytes /= 4; else printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); abinfo.fragments = jz_audio_fragments; abinfo.fragstotal = jz_audio_fragstotal; if (jz_audio_channels == 2) abinfo.fragsize = jz_audio_fragsize / jz_audio_b; else if (jz_audio_channels == 1) abinfo.fragsize = jz_audio_fragsize / 4; else printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); abinfo.bytes = (int)bytes; return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; } case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && in_dma_buf) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && out_dma_buf) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; return 0; case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); cinfo.bytes = controller->total_bytes; cinfo.blocks = controller->blocks; cinfo.ptr = controller->nextIn; controller->blocks = 0; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); cinfo.bytes = controller->total_bytes; cinfo.blocks = controller->blocks; cinfo.ptr = controller->nextOut; controller->blocks = 0; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); unfinish = 0; fullc = elements_in_queue(&out_full_queue); busyc = elements_in_queue(&out_busy_queue); for(i = 0;i < fullc ;i ++) { id = *(out_full_queue.id + i); unfinish += *(out_dma_buf_data_count + id); } for(i = 0;i < busyc ;i ++) { id = *(out_busy_queue.id + i); unfinish += get_dma_residue(controller->dma1); } //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); if (jz_audio_channels == 2) unfinish /= jz_audio_b; else if (jz_audio_channels == 1) unfinish /= 4; else printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); return put_user(unfinish, (int *) arg); case SOUND_PCM_READ_RATE: return put_user(jz_audio_rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: return put_user(jz_audio_channels, (int *)arg); case SOUND_PCM_READ_BITS: return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return -EINVAL; } return -EINVAL; } static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) { struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; unsigned int mask = 0; if (file->f_mode & FMODE_WRITE) { if (elements_in_queue(&out_empty_queue) > 0) return POLLOUT | POLLWRNORM; poll_wait(file, &controller->dac_wait, wait); } if (file->f_mode & FMODE_READ) { if (elements_in_queue(&in_full_queue) > 0) return POLLIN | POLLRDNORM; poll_wait(file, &controller->adc_wait, wait); } //spin_lock_irqsave(&controller->lock, flags); spin_lock(&controller->lock); if (file->f_mode & FMODE_WRITE) { if (elements_in_queue(&out_empty_queue) > 0) mask |= POLLOUT | POLLWRNORM; } else if (file->f_mode & FMODE_READ) { if (elements_in_queue(&in_full_queue) > 0) mask |= POLLIN | POLLRDNORM; } //spin_unlock_irqrestore(&controller->lock, flags); spin_unlock(&controller->lock); return mask; } static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; int id, ret = 0, left_count, copy_count, cnt = 0; if (count < 0) return -EINVAL; __i2s_enable_receive_dma(); __i2s_enable_record(); //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); controller->nextIn = 0; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); copy_count = jz_audio_fragsize / 4; left_count = count; if (first_record_call) { first_record_call = 0; audio_read_back_first: if ((id = get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_busy_queue, id); spin_lock(&controller->lock); *(in_dma_buf_data_count + id) = copy_count * 4; spin_unlock(&controller->lock); __i2s_enable_receive_dma(); __i2s_enable_record(); dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); audio_start_dma(controller->dma2,file->private_data, *(in_dma_pbuf + id), *(in_dma_buf_data_count + id), DMA_MODE_READ); sleep_on(&rx_wait_queue); } else goto audio_read_back_first; } while (left_count > 0) { audio_read_back_second: if (elements_in_queue(&in_full_queue) <= 0) { if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; else sleep_on(&rx_wait_queue); } if ((id = get_buffer_id(&in_full_queue)) >= 0) { spin_lock(&controller->lock); cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); spin_unlock(&controller->lock); put_buffer_id(&in_empty_queue, id); } else goto audio_read_back_second; if (elements_in_queue(&in_busy_queue) == 0) { if ((id=get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_busy_queue, id); spin_lock(&controller->lock); *(in_dma_buf_data_count + id) = copy_count * 4; spin_unlock(&controller->lock); dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); audio_start_dma(controller->dma2,file->private_data, *(in_dma_pbuf + id), *(in_dma_buf_data_count + id), DMA_MODE_READ); } } if (ret + cnt > count) { spin_lock(&controller->lock); cnt = count - ret; spin_unlock(&controller->lock); } if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) return ret ? ret : -EFAULT; spin_lock(&controller->lock); ret += cnt; spin_unlock(&controller->lock); //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); controller->nextIn += ret; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); spin_lock(&controller->lock); left_count -= cnt; spin_unlock(&controller->lock); } return ret; } static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { int id, ret = 0, left_count, copy_count = 0; struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; if (count <= 0) return -EINVAL; if(set_replay_hp_or_speaker) set_replay_hp_or_speaker(); __i2s_enable_transmit_dma(); __i2s_enable_replay(); //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); controller->nextOut = 0; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); if (jz_audio_channels == 2) copy_count = jz_audio_fragsize / jz_audio_b; else if(jz_audio_channels == 1) copy_count = jz_audio_fragsize / 4; left_count = count; if (copy_from_user(controller->tmp1, buffer, count)) { printk("copy_from_user failed:%d",ret); return ret ? ret : -EFAULT; } while (left_count > 0) { audio_write_back: if (file->f_flags & O_NONBLOCK) udelay(2); if (elements_in_queue(&out_empty_queue) == 0) { if (file->f_flags & O_NONBLOCK) return ret; else sleep_on(&tx_wait_queue); } /* the end fragment size in this write */ if (ret + copy_count > count) copy_count = count - ret; if ((id = get_buffer_id(&out_empty_queue)) >= 0) { replay_filler((signed long)controller->tmp1 + ret, copy_count, id); if(*(out_dma_buf_data_count + id) > 0) { put_buffer_id(&out_full_queue, id); dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); } else put_buffer_id(&out_empty_queue, id); } else goto audio_write_back; left_count = left_count - copy_count; ret += copy_count; //spin_lock_irqsave(&controller->ioctllock, flags); spin_lock(&controller->ioctllock); controller->nextOut += ret; //spin_unlock_irqrestore(&controller->ioctllock, flags); spin_unlock(&controller->ioctllock); if (elements_in_queue(&out_busy_queue) == 0) { if ((id=get_buffer_id(&out_full_queue)) >= 0) { put_buffer_id(&out_busy_queue, id); if(*(out_dma_buf_data_count + id) > 0) { audio_start_dma(controller->dma1, file->private_data, *(out_dma_pbuf + id), *(out_dma_buf_data_count + id), DMA_MODE_WRITE); last_dma_buffer_id = id; #if defined(CONFIG_I2S_DLV) if (jz_codec_config == 0) { write_codec_file_bit(1, 0, 5); gain_up_start = jiffies; sleep_on(&pop_wait_queue); //gain_up_end = jiffies; jz_codec_config = 1; //SB_ADC->1 //write_codec_file_bit(5, 1, 4); //while(1); } #endif } } } } return ret; } #if defined(CONFIG_I2S_ICODEC) static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) { int i,step_len; unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; unsigned long l_sample_count,r_sample_count,sample_count; struct jz_i2s_controller_info *controller = i2s_controller; signed int left_sam=0,right_sam=0,l_val,r_val; switch (sample_oss) { case 0x0: break; case 0x1: left_sam = (signed int)l_sample; right_sam = (signed int)r_sample; break; case 0x2: break; case 0x3: break; case 0x4: break; } if(left_sam == 0 && right_sam == 0) return; switch (sample_oss) { case 0x0: break; case 0x1: step_len = jz_audio_speed / 10 * 3; step_len = step_len / 2; step_len = 0x7fff / step_len + 1; l_sample_count = 0; l_val = left_sam; while(1) { if(l_val > 0) { if(l_val >= step_len) { l_val -= step_len; l_sample_count ++; } else break; } if(l_val < 0) { if(l_val <= -step_len) { l_val += step_len; l_sample_count ++; } else break; } if(l_val == 0) break; } r_sample_count = 0; r_val = right_sam; while(1) { if(r_val > 0) { if(r_val >= step_len) { r_val -= step_len; r_sample_count ++; } else break; } if(r_val < 0) { if(r_val <= -step_len) { r_val += step_len; r_sample_count ++; } else break; } if(r_val == 0) break; } /* fill up */ if(l_sample_count > r_sample_count) sample_count = l_sample_count; else sample_count = r_sample_count; l_val = left_sam; r_val = right_sam; for(i=0;i <= sample_count;i++) { *pop_buf = (unsigned long)l_val; pop_buf ++; if(l_val > step_len) l_val -= step_len; else if(l_val < -step_len) l_val += step_len; else if(l_val >= -step_len && l_val <= step_len) l_val = 0; *pop_buf = (unsigned long)r_val; pop_buf ++; if(r_val > step_len) r_val -= step_len; else if(r_val < -step_len) r_val += step_len; else if(r_val >= -step_len && r_val <= step_len) r_val = 0; } *pop_buf = 0; pop_buf ++; *pop_buf = 0; pop_buf ++; sample_count += 2; dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); pop_dma_flag = 1; audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); sleep_on(&pop_wait_queue); pop_dma_flag = 0; break; case 0x2: break; case 0x3: break; case 0x4: break; } } #endif