📄 ac97.c
字号:
if (REG32(AC97_ACSR) & AIC_ACSR_CADT) { REG32(AC97_ACSR) &= ~AIC_ACSR_CADT; done = 1; } else udelay (1); } return done;} static inline int jz_in_status_ready(void){ int t2 = 1000; int done = 0; while (! done && t2-- > 0) if (REG32(AC97_ACSR) & AIC_ACSR_SADR) { REG32(AC97_ACSR) &= ~AIC_ACSR_SADR; done = 1; } else { if (REG32(AC97_ACSR) & AIC_ACSR_RSTO) { REG32(AC97_ACSR) &= ~AIC_ACSR_RSTO; done = 0; break; } udelay (1); } return done;}static int jz_readAC97Reg (u8 reg){ u16 value; if (reg < 128) { u8 ret_reg; __ac97_out_rcmd_addr(reg); if (jz_out_command_ready()) while (jz_in_status_ready()) { ret_reg = __ac97_in_status_addr(); value = __ac97_in_data(); if (ret_reg == reg) return value; else return -1; } } value = __ac97_in_data(); return -1;}static int jz_writeAC97Reg (u8 reg, u16 value){ unsigned long flags; int done = 0; flags = spin_lock_irqsave(); __ac97_out_wcmd_addr(reg); __ac97_out_data(value); if (jz_out_command_ready()) done = 1; spin_unlock_irqrestore(flags); return done;}u16 ac97_codec_read(u8 reg){ int res = jz_readAC97Reg(reg); int count = 0; while (res == -1) { udelay(1000); __ac97_warm_reset_codec(); udelay(1000); res = jz_readAC97Reg(reg); count ++; if (count > MAX_RETRY) break; } return (u16)res;}void ac97_codec_write(u8 reg, u16 data){ int done = jz_writeAC97Reg(reg, data); int count = 0; while (done == 0) { count ++; udelay (2000); __ac97_warm_reset_codec(); udelay(2000); done = jz_writeAC97Reg(reg, data); if ( count > MAX_RETRY ) break; }}/* AC97 codec initialisation. */static int jz_ac97_codec_init(void){ int num_ac97 = 0; int ready_2nd = 0; unsigned short eid, t; int i = 0; if (__ac97_codec_is_low_power_mode()) { printf("AC97 codec is low power mode, warm reset ...\n"); __ac97_warm_reset_codec(); udelay(10); } i = 0; while (!__ac97_codec_is_ready()) { i++; if ( i > 100) { printf("AC97 codec not ready, failed init ..\n"); return -1; } udelay(10); } i = 0; /* AC97 probe codec */ ac97_codec_write(AC97_RESET, 0L); udelay(10); if (ac97_codec_read(AC97_RESET) & 0x8000) { printf("AC97 codec not present...\n"); return -1; } ac97_codec_write(AC97_EXTENDED_MODEM_ID, 0L); /* Probe for Modem Codec */ ac97_codec_read(AC97_EXTENDED_MODEM_ID); ac97codec_info(ac97_codec_read(AC97_VENDOR_ID1), ac97_codec_read(AC97_VENDOR_ID2)); /* Codec capacity */ t = ac97_codec_read(AC97_RESET); printf("Support:"); if (t & 0x04) printf(" BASS TREBLE"); if (t & 0x10) printf(" ALTPCM"); printf("\n"); ac97_codec_write(AC97_MASTER_VOL_STEREO, 0x2020); if (ac97_codec_read(AC97_MASTER_VOL_STEREO) == 0x2020) bitscale = 6; else bitscale = 5; printf("AC97 Master Volume bit resolution: %dbit\n", bitscale); eid = ac97_codec_read(AC97_EXTENDED_ID); if (eid == 0xFFFF) { printf("AC97: no codec attached?\n"); return -1; } if (!(eid & 0x0001)) printf("AC97 codec: only supports fixed 48KHz sample rate\n"); else { printf("AC97 codec: supports variable sample rate\n"); /* enable head phone */ ac97_codec_write(0x6a, ac97_codec_read(0x6a)|0x40);#if 0 ac97_codec_write(AC97_EXTENDED_STATUS, 9); ac97_codec_write(AC97_EXTENDED_STATUS, ac97_codec_read(AC97_EXTENDED_STATUS)|0xe800); #endif ac97_codec_write(AC97_POWER_CONTROL, ac97_codec_read(AC97_POWER_CONTROL)&~0x7f00); for (i=10;i;i--) if ((ac97_codec_read(AC97_POWER_CONTROL) & 0xf) == 0xf) break;#if 0 if (!ac97_codec_read(AC97_EXTENDED_STATUS) & 1) printf("Codec refuse to support VRA, using 48Khz\n");#endif } return 0;}static void jz_update_filler(int format, int channels){#define TYPE(fmt,ch) (((fmt)<<3) | ((ch)&7)) /* up to 8 chans supported. */ switch (TYPE(format, channels)) { default: case TYPE(AFMT_U8, 1): __aic_disable_mono2stereo(); __aic_disable_unsignadj(); dma_block_size(AUDIO_READ_DMA, 4); dma_block_size(AUDIO_WRITE_DMA, 4); replay_filler = replay_fill_1x8_u; record_filler = record_fill_1x8_u; __ac97_set_oass(8); __ac97_set_iass(8); dma_src_size(AUDIO_READ_DMA, 8); dma_dest_size(AUDIO_WRITE_DMA, 8); break; case TYPE(AFMT_U8, 2): __aic_disable_mono2stereo(); __aic_disable_unsignadj(); dma_block_size(AUDIO_READ_DMA, 4); dma_block_size(AUDIO_WRITE_DMA, 4); replay_filler = replay_fill_2x8_u; record_filler = record_fill_2x8_u; __ac97_set_oass(8); __ac97_set_iass(8); dma_src_size(AUDIO_READ_DMA, 8); dma_dest_size(AUDIO_WRITE_DMA, 8); break; case TYPE(AFMT_S16_LE, 1): __aic_disable_mono2stereo(); dma_block_size(AUDIO_READ_DMA, 4); dma_block_size(AUDIO_WRITE_DMA, 4); __aic_disable_unsignadj(); replay_filler = replay_fill_1x16_s; record_filler = record_fill_1x16_s; __ac97_set_oass(16); __ac97_set_iass(16); dma_src_size(AUDIO_READ_DMA, 16); dma_dest_size(AUDIO_WRITE_DMA, 16); break; case TYPE(AFMT_S16_LE, 2): dma_block_size(AUDIO_READ_DMA, 4); dma_block_size(AUDIO_WRITE_DMA, 4); __aic_disable_mono2stereo(); __aic_disable_unsignadj(); replay_filler = replay_fill_2x16_s; record_filler = record_fill_2x16_s; __ac97_set_oass(16); __ac97_set_iass(16); dma_src_size(AUDIO_READ_DMA, 16); dma_dest_size(AUDIO_WRITE_DMA, 16); break; }}int pcm_ioctl(unsigned int cmd, unsigned long arg){ switch (cmd) { case PCM_SET_SAMPLE_RATE: jz_audio_set_speed(arg); break; case PCM_SET_CHANNEL: jz_audio_set_channels(arg); break; case PCM_SET_FORMAT: jz_audio_set_format(arg); break; case PCM_SET_VOL: if (arg > 100) arg = 100; arg = 100 - arg; arg = arg * ((1 << bitscale) - 1) / 100; arg = (arg << 8) | arg; ac97_codec_write(AC97_MASTER_VOL_STEREO, arg); ac97_codec_write(AC97_PCMOUT_VOL, arg); break; case PCM_GET_VOL: ac97_codec_read(AC97_MASTER_VOL_STEREO); break; default: printf("pcm_ioctl:Unsupported I/O command: %08x\n", cmd); return -1; } return 0;}int pcm_can_write(void){ if (elements_in_queue(&out_empty_queue) > 0) return 1; return 0;}int pcm_can_read(void){ if (elements_in_queue(&in_full_queue) > 0) return 1; return 0;}int pcm_read(char *buffer, int count){ int id, ret = 0, left_count, copy_count, cnt = 0; U8 err; if (count < 0) return -1; if (count < 2*PAGE_SIZE / jz_audio_k) { copy_count = count * 16 / (jz_audio_channels * jz_audio_format); } else copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; left_count = count; if (first_record_call) { first_record_call = 0; if ((id = get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_busy_queue, id); in_dma_buf_data_count[id] = copy_count * (jz_audio_format/8); __ac97_enable_receive_dma(); __ac97_enable_record(); dma_start(AUDIO_READ_DMA, PHYADDR(AIC_DR), in_dma_pbuf[id], in_dma_buf_data_count[id]); OSSemPend(rx_sem, 0, &err); } } while (left_count > 0) { while (elements_in_queue(&in_full_queue) <= 0) OSSemPend(rx_sem, 0, &err); if ((id = get_buffer_id(&in_full_queue)) >= 0) { /* * FIXME: maybe the buffer is too small. */ cnt = record_filler((unsigned long)buffer+ret, copy_count, id); put_buffer_id(&in_empty_queue, id); } if (elements_in_queue(&in_busy_queue) == 0) { if ((id=get_buffer_id(&in_empty_queue)) >= 0) { put_buffer_id(&in_busy_queue, id); in_dma_buf_data_count[id] = copy_count * (jz_audio_format/8); __ac97_enable_receive_dma(); __ac97_enable_record(); dma_start(AUDIO_READ_DMA, PHYADDR(AIC_DR), in_dma_pbuf[id], in_dma_buf_data_count[id]); } } if (ret + cnt > count) cnt = count - ret; ret += cnt; left_count -= cnt; } return ret;}int pcm_write(char *buffer, int count){ int id, ret = 0, left_count, copy_count; U8 err; if (count <= 0) return -1; /* The data buffer size of the user space is always a PAGE_SIZE * scale, so the process can be simplified. */ if (count < 2*PAGE_SIZE / jz_audio_k) copy_count = count; else copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; left_count = count; while (left_count > 0) { while (elements_in_queue(&out_empty_queue) == 0) OSSemPend(tx_sem, 0, &err); /* 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) { /* * FIXME: maybe the buffer is too small. */ replay_filler( (unsigned long)buffer + ret, copy_count, id); put_buffer_id(&out_full_queue, id); __dcache_writeback_all(); } left_count = left_count - copy_count; ret += copy_count; if (elements_in_queue(&out_busy_queue) == 0) { if ((id=get_buffer_id(&out_full_queue)) >= 0) { put_buffer_id(&out_busy_queue, id); __ac97_enable_transmit_dma(); __ac97_enable_replay(); dma_start(AUDIO_WRITE_DMA, out_dma_pbuf[id], PHYADDR(AIC_DR), out_dma_buf_data_count[id]); } } } return ret;}static unsigned char dma_pool[PAGE_SIZE * (16 *( QUEUE_MAX + 1)*2)];int pcm_init(void){ int i; unsigned char *p = dma_pool; jz_ac97_initHw(); if (jz_ac97_codec_init() < 0) return -1; tx_sem = OSSemCreate(0); rx_sem = OSSemCreate(0); dma_request(AUDIO_READ_DMA, jz_ac97_record_dma_irq, 0, DMAC_DCCSR_DAM|DMAC_DCCSR_RDIL_IGN, DMAC_DRSR_RS_AICIN); dma_request(AUDIO_WRITE_DMA, jz_ac97_replay_dma_irq, 0, DMAC_DCCSR_SAM|DMAC_DCCSR_RDIL_IGN, DMAC_DRSR_RS_AICOUT); jz_audio_reset(); jz_audio_set_format(AFMT_U8); jz_audio_set_speed(8000); jz_audio_set_channels(1); last_jz_audio_count = jz_audio_count = 0; in_empty_queue.count = QUEUE_MAX; out_empty_queue.count = QUEUE_MAX; for (i=0;i<QUEUE_MAX;i++) { in_empty_queue.id[i] = i; out_empty_queue.id[i] = i; } in_full_queue.count = 0; in_busy_queue.count = 0; out_busy_queue.count = 0; out_full_queue.count = 0; /* Aligned in PAGE_SIZE */ p = (unsigned char *)(((unsigned int)p + PAGE_SIZE) & ~(PAGE_SIZE-1)); for (i = 0; i < QUEUE_MAX; i++) { out_dma_buf[i] = (unsigned long)p; p += PAGE_SIZE*16; out_dma_pbuf[i] = PHYADDR((unsigned int)out_dma_buf[i]); in_dma_buf[i] = (unsigned long)p; p += PAGE_SIZE*16; in_dma_pbuf[i] = PHYADDR((unsigned int)in_dma_buf[i]); } pcm_ioctl(PCM_SET_SAMPLE_RATE, 16000); pcm_ioctl(PCM_SET_FORMAT, AFMT_S16_LE); pcm_ioctl(PCM_SET_CHANNEL, 2); pcm_ioctl(PCM_SET_VOL, 100); /* 100% */ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -