📄 cs4218_tdm.c
字号:
static void CS_Init(void){ int i, tolerance; switch (sound.soft.format) { case AFMT_S16_LE: case AFMT_U16_LE: sound.hard.format = AFMT_S16_LE; break; default: sound.hard.format = AFMT_S16_BE; break; } sound.hard.stereo = 1; sound.hard.size = 16; /* * If we have a sample rate which is within catchRadius percent * of the requested value, we don't have to expand the samples. * Otherwise choose the next higher rate. */ i = (sizeof(cs4218_freqs) / sizeof(int)); do { tolerance = catchRadius * cs4218_freqs[--i] / 100; } while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0); if (sound.soft.speed >= cs4218_freqs[i] - tolerance) sound.trans_write = &transCSNormal; else sound.trans_write = &transCSExpand; sound.trans_read = &transCSNormalRead; sound.hard.speed = cs4218_freqs[i]; cs4218_rate_index = i; /* The CS4218 has seven selectable clock dividers for the sample * clock. The HIOX then provides one of two external rates. * An even numbered frequency table index uses the high external * clock rate. */ *(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL); if ((i & 1) == 0) *(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI; i >>= 1; *(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL); expand_bal = -sound.soft.speed;}static int CS_SetFormat(int format){ int size; switch (format) { case AFMT_QUERY: return sound.soft.format; case AFMT_MU_LAW: case AFMT_A_LAW: case AFMT_U8: case AFMT_S8: size = 8; break; case AFMT_S16_BE: case AFMT_U16_BE: case AFMT_S16_LE: case AFMT_U16_LE: size = 16; break; default: /* :-) */ printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", format); size = 8; format = AFMT_U8; } sound.soft.format = format; sound.soft.size = size; if (sound.minDev == SND_DEV_DSP) { sound.dsp.format = format; sound.dsp.size = size; } CS_Init(); return format;}/* Volume is the amount of attenuation we tell the codec to impose * on the outputs. There are 32 levels, with 0 the "loudest". */#define CS_VOLUME_TO_MASK(x) (31 - ((((x) - 1) * 31) / 99))#define CS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 31))static int cs_get_volume(uint reg){ int volume; volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg)); volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8; return volume;}static int cs_volume_setter(int volume, int mute){ uint tempctl; if (mute && volume == 0) { tempctl = cs4218_control | CS_MUTE; } else { tempctl = cs4218_control & ~CS_MUTE; tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN); tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff)); tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff)); volume = cs_get_volume(tempctl); } if (tempctl != cs4218_control) { cs4218_ctl_write(tempctl); } return volume;}/* Gain has 16 steps from 0 to 15. These are in 1.5dB increments from * 0 (no gain) to 22.5 dB. */#define CS_RECLEVEL_TO_GAIN(v) \ ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)#define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3)static int cs_get_gain(uint reg){ int gain; gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg)); gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8; return gain;}static int cs_set_gain(int gain){ uint tempctl; tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN); tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff)); tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff)); gain = cs_get_gain(tempctl); if (tempctl != cs4218_control) { cs4218_ctl_write(tempctl); } return gain;}static int CS_SetVolume(int volume){ return cs_volume_setter(volume, CS_MUTE);}static void CS_Play(void){ int i, count; unsigned long flags; volatile cbd_t *bdp; volatile cpm8xx_t *cp; save_flags(flags); cli();#if 0 if (awacs_beep_state) { /* sound takes precedence over beeps */ out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) | (awacs_rate_index << 8)); out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); beep_playing = 0; awacs_beep_state = 0; }#endif i = sq.front + sq.active; if (i >= sq.max_count) i -= sq.max_count; while (sq.active < 2 && sq.active < sq.count) { count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; if (count < sq.block_size && !sq.syncing) /* last block not yet filled, and we're not syncing. */ break; bdp = &tx_base[i]; bdp->cbd_datlen = count; flush_dcache_range((ulong)sound_buffers[i], (ulong)(sound_buffers[i] + count)); if (++i >= sq.max_count) i = 0; if (sq.active == 0) { /* The SMC does not load its fifo until the first * TDM frame pulse, so the transmit data gets shifted * by one word. To compensate for this, we incorrectly * transmit the first buffer and shorten it by one * word. Subsequent buffers are then aligned properly. */ bdp->cbd_datlen -= 2; /* Start up the SMC Transmitter. */ cp = cpmp; cp->cp_smc[1].smc_smcmr |= SMCMR_TEN; cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_RESTART_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); } /* Buffer is ready now. */ bdp->cbd_sc |= BD_SC_READY; ++sq.active; } restore_flags(flags);}static void CS_Record(void){ unsigned long flags; volatile smc_t *sp; if (read_sq.active) return; save_flags(flags); cli(); /* This is all we have to do......Just start it up. */ sp = &cpmp->cp_smc[1]; sp->smc_smcmr |= SMCMR_REN; read_sq.active = 1; restore_flags(flags);}static voidcs4218_tdm_tx_intr(void *devid){ int i = sq.front; volatile cbd_t *bdp; while (sq.active > 0) { bdp = &tx_base[i]; if (bdp->cbd_sc & BD_SC_READY) break; /* this frame is still going */ --sq.count; --sq.active; if (++i >= sq.max_count) i = 0; } if (i != sq.front) WAKE_UP(sq.action_queue); sq.front = i; CS_Play(); if (!sq.active) WAKE_UP(sq.sync_queue);}static voidcs4218_tdm_rx_intr(void *devid){ /* We want to blow 'em off when shutting down. */ if (read_sq.active == 0) return; /* Check multiple buffers in case we were held off from * interrupt processing for a long time. Geeze, I really hope * this doesn't happen. */ while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) { /* Invalidate the data cache range for this buffer. */ invalidate_dcache_range( (uint)(sound_read_buffers[read_sq.rear]), (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size)); /* Make buffer available again and move on. */ rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY; read_sq.rear++; /* Wrap the buffer ring. */ if (read_sq.rear >= read_sq.max_active) read_sq.rear = 0; /* If we have caught up to the front buffer, bump it. * This will cause weird (but not fatal) results if the * read loop is currently using this buffer. The user is * behind in this case anyway, so weird things are going * to happen. */ if (read_sq.rear == read_sq.front) { read_sq.front++; if (read_sq.front >= read_sq.max_active) read_sq.front = 0; } } WAKE_UP(read_sq.action_queue);}static void cs_nosound(unsigned long xx){ unsigned long flags; save_flags(flags); cli(); if (beep_playing) {#if 0 st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);#endif beep_playing = 0; } restore_flags(flags);}static struct timer_list beep_timer = { function: cs_nosound};static void cs_mksound(unsigned int hz, unsigned int ticks){ unsigned long flags; int beep_speed = BEEP_SPEED; int srate = cs4218_freqs[beep_speed]; int period, ncycles, nsamples; int i, j, f; short *p; static int beep_hz_cache; static int beep_nsamples_cache; static int beep_volume_cache; if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {#if 1 /* this is a hack for broken X server code */ hz = 750; ticks = 12;#else /* cancel beep currently playing */ awacs_nosound(0); return;#endif } save_flags(flags); cli(); del_timer(&beep_timer); if (ticks) { beep_timer.expires = jiffies + ticks; add_timer(&beep_timer); } if (beep_playing || sq.active || beep_buf == NULL) { restore_flags(flags); return; /* too hard, sorry :-( */ } beep_playing = 1;#if 0 st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);#endif restore_flags(flags); if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { nsamples = beep_nsamples_cache; } else { period = srate * 256 / hz; /* fixed point */ ncycles = BEEP_BUFLEN * 256 / period; nsamples = (period * ncycles) >> 8; f = ncycles * 65536 / nsamples; j = 0; p = beep_buf; for (i = 0; i < nsamples; ++i, p += 2) { p[0] = p[1] = beep_wform[j >> 8] * beep_volume; j = (j + f) & 0xffff; } beep_hz_cache = hz; beep_volume_cache = beep_volume; beep_nsamples_cache = nsamples; }#if 0 st_le16(&beep_dbdma_cmd->req_count, nsamples*4); st_le16(&beep_dbdma_cmd->xfer_status, 0); st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); awacs_beep_state = 1; save_flags(flags); cli(); if (beep_playing) { /* i.e. haven't been terminated already */ out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) | (beep_speed << 8)); out_le32(&awacs->byteswap, 0); out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); out_le32(&awacs_txdma->control, RUN | (RUN << 16)); }#endif restore_flags(flags);}static void CS_open(void){ MOD_INC_USE_COUNT;}static void CS_release(void){ MOD_DEC_USE_COUNT;}static MACHINE mach_cs4218 = { name: "HIOX CS4218", name2: "Built-in Sound", open: CS_open, release: CS_release, dma_alloc: CS_Alloc, dma_free: CS_Free, irqinit: CS_IrqInit,#ifdef MODULE irqcleanup: CS_IrqCleanup,#endif /* MODULE */ init: CS_Init, silence: CS_Silence, setFormat: CS_SetFormat, setVolume: CS_SetVolume, play: CS_Play};/*** Mid level stuff *********************************************************/static void sound_silence(void){ /* update hardware settings one more */ (*sound.mach.init)(); (*sound.mach.silence)();}static void sound_init(void){ (*sound.mach.init)();}static int sound_set_format(int format){ return(*sound.mach.setFormat)(format);}static int sound_set_speed(int speed){ if (speed < 0) return(sound.soft.speed); sound.soft.speed = speed; (*sound.mach.init)(); if (sound.minDev == SND_DEV_DSP) sound.dsp.speed = sound.soft.speed; return(sound.soft.speed);}static int sound_set_stereo(int stereo){ if (stereo < 0) return(sound.soft.stereo); stereo = !!stereo; /* should be 0 or 1 now */ sound.soft.stereo = stereo; if (sound.minDev == SND_DEV_DSP) sound.dsp.stereo = stereo; (*sound.mach.init)(); return(stereo);}static int sound_set_volume(int volume){ return(*sound.mach.setVolume)(volume);}static ssize_t sound_copy_translate(const u_char *userPtr, size_t userCount, u_char frame[], ssize_t *frameUsed, ssize_t frameLeft){ ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; switch (sound.soft.format) { case AFMT_MU_LAW: ct_func = sound.trans_write->ct_ulaw; break; case AFMT_A_LAW: ct_func = sound.trans_write->ct_alaw; break; case AFMT_S8: ct_func = sound.trans_write->ct_s8; break; case AFMT_U8: ct_func = sound.trans_write->ct_u8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -