📄 hal2.c
字号:
pbus->pbus->pbdma_dptr = hal2_desc_addr(dac, dac->tail); pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; /* enable DAC */ hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);}static void hal2_start_adc(struct hal2_card *hal2){ struct hal2_codec *adc = &hal2->adc; struct hal2_pbus *pbus = &adc->pbus; pbus->pbus->pbdma_dptr = hal2_desc_addr(adc, adc->head); pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; /* enable ADC */ hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);}static inline void hal2_stop_dac(struct hal2_card *hal2){ hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; /* The HAL2 itself may remain enabled safely */}static inline void hal2_stop_adc(struct hal2_card *hal2){ hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;}static int hal2_alloc_dmabuf(struct hal2_codec *codec, int size, int count, int cntinfo, int dir){ struct hal2_desc *desc, *dma_addr; int i; DEBUG("allocating %dk DMA buffer.\n", size / 1024); codec->buffer = (unsigned char *)__get_free_pages(GFP_KERNEL | GFP_DMA, get_order(size)); if (!codec->buffer) return -ENOMEM; desc = dma_alloc_coherent(NULL, count * sizeof(struct hal2_desc), (dma_addr_t *)&dma_addr, GFP_KERNEL); if (!desc) { free_pages((unsigned long)codec->buffer, get_order(size)); return -ENOMEM; } codec->desc = desc; for (i = 0; i < count; i++) { desc->desc.pbuf = dma_map_single(NULL, (void *)(codec->buffer + i * H2_BLOCK_SIZE), H2_BLOCK_SIZE, dir); desc->desc.cntinfo = cntinfo; desc->desc.pnext = (i == count - 1) ? (u32)dma_addr : (u32)(dma_addr + i + 1); desc->cnt = 0; desc++; } codec->desc_count = count; codec->head = codec->tail = 0; return 0;}static int hal2_alloc_dac_dmabuf(struct hal2_codec *codec){ return hal2_alloc_dmabuf(codec, H2_DAC_BUFSIZE, H2_DAC_BUFSIZE / H2_BLOCK_SIZE, HPCDMA_XIE | HPCDMA_EOX, DMA_TO_DEVICE);}static int hal2_alloc_adc_dmabuf(struct hal2_codec *codec){ return hal2_alloc_dmabuf(codec, H2_ADC_BUFSIZE, H2_ADC_BUFSIZE / H2_BLOCK_SIZE, HPCDMA_XIE | H2_BLOCK_SIZE, DMA_TO_DEVICE);}static void hal2_free_dmabuf(struct hal2_codec *codec, int size, int dir){ dma_addr_t dma_addr; int i; dma_addr = codec->desc[codec->desc_count - 1].desc.pnext; for (i = 0; i < codec->desc_count; i++) dma_unmap_single(NULL, codec->desc[i].desc.pbuf, H2_BLOCK_SIZE, dir); dma_free_coherent(NULL, codec->desc_count * sizeof(struct hal2_desc), (void *)codec->desc, dma_addr); free_pages((unsigned long)codec->buffer, get_order(size));}static void hal2_free_dac_dmabuf(struct hal2_codec *codec){ return hal2_free_dmabuf(codec, H2_DAC_BUFSIZE, DMA_TO_DEVICE);}static void hal2_free_adc_dmabuf(struct hal2_codec *codec){ return hal2_free_dmabuf(codec, H2_ADC_BUFSIZE, DMA_FROM_DEVICE);}/* * Add 'count' bytes to 'buffer' from DMA ring buffers. Return number of * bytes added or -EFAULT if copy_from_user failed. */static int hal2_get_buffer(struct hal2_card *hal2, char *buffer, int count){ unsigned long flags; int size, ret = 0; unsigned char *buf; struct hal2_desc *tail; struct hal2_codec *adc = &hal2->adc; DEBUG("getting %d bytes ", count); spin_lock_irqsave(&adc->lock, flags); tail = &adc->desc[adc->tail]; /* enable DMA stream if there are no data */ if (!tail->cnt && !(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT)) hal2_start_adc(hal2); while (tail->cnt > 0 && count > 0) { size = min((int)tail->cnt, count); buf = &adc->buffer[(adc->tail + 1) * H2_BLOCK_SIZE - tail->cnt]; spin_unlock_irqrestore(&adc->lock, flags); dma_sync_single(NULL, tail->desc.pbuf, size, DMA_FROM_DEVICE); if (copy_to_user(buffer, buf, size)) { ret = -EFAULT; goto out; } spin_lock_irqsave(&adc->lock, flags); tail->cnt -= size; /* buffer is empty, update tail pointer */ if (tail->cnt == 0) { tail->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE; hal2_inc_tail(adc); tail = &adc->desc[adc->tail]; /* enable DMA stream again if needed */ if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT)) hal2_start_adc(hal2); } buffer += size; ret += size; count -= size; DEBUG("(%d) ", size); } spin_unlock_irqrestore(&adc->lock, flags);out: DEBUG("\n"); return ret;} /* * Add 'count' bytes from 'buffer' to DMA ring buffers. Return number of * bytes added or -EFAULT if copy_from_user failed. */static int hal2_add_buffer(struct hal2_card *hal2, char *buffer, int count){ unsigned long flags; unsigned char *buf; int size, ret = 0; struct hal2_desc *head; struct hal2_codec *dac = &hal2->dac; DEBUG("adding %d bytes ", count); spin_lock_irqsave(&dac->lock, flags); head = &dac->desc[dac->head]; while (head->cnt == 0 && count > 0) { size = min((int)H2_BLOCK_SIZE, count); buf = &dac->buffer[dac->head * H2_BLOCK_SIZE]; spin_unlock_irqrestore(&dac->lock, flags); if (copy_from_user(buf, buffer, size)) { ret = -EFAULT; goto out; } dma_sync_single(NULL, head->desc.pbuf, size, DMA_TO_DEVICE); spin_lock_irqsave(&dac->lock, flags); head->desc.cntinfo = size | HPCDMA_XIE; head->cnt = size; buffer += size; ret += size; count -= size; hal2_inc_head(dac); head = &dac->desc[dac->head]; DEBUG("(%d) ", size); } if (!(dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && ret > 0) hal2_start_dac(hal2); spin_unlock_irqrestore(&dac->lock, flags);out: DEBUG("\n"); return ret;}#define hal2_reset_dac_pointer(hal2) hal2_reset_pointer(hal2, 1)#define hal2_reset_adc_pointer(hal2) hal2_reset_pointer(hal2, 0)static void hal2_reset_pointer(struct hal2_card *hal2, int is_dac){ int i; struct hal2_codec *codec = (is_dac) ? &hal2->dac : &hal2->adc; DEBUG("hal2_reset_pointer\n"); for (i = 0; i < codec->desc_count; i++) { codec->desc[i].cnt = 0; codec->desc[i].desc.cntinfo = HPCDMA_XIE | (is_dac) ? HPCDMA_EOX : H2_BLOCK_SIZE; } codec->head = codec->tail = 0;}static int hal2_sync_dac(struct hal2_card *hal2){ DECLARE_WAITQUEUE(wait, current); struct hal2_codec *dac = &hal2->dac; int ret = 0; unsigned long flags; signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * dac->voices * HZ / dac->sample_rate / 900; while (dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) { add_wait_queue(&dac->dma_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(timeout); spin_lock_irqsave(&dac->lock, flags); if (dac->desc[dac->tail].cnt) ret = -ETIME; spin_unlock_irqrestore(&dac->lock, flags); if (signal_pending(current)) ret = -ERESTARTSYS; if (ret) { hal2_stop_dac(hal2); hal2_reset_dac_pointer(hal2); } remove_wait_queue(&dac->dma_wait, &wait); } return ret;}static int hal2_write_mixer(struct hal2_card *hal2, int index, int vol){ unsigned int l, r, tmp; DEBUG_MIX("mixer %d write\n", index); if (index >= SOUND_MIXER_NRDEVICES || !mixtable[index].avail) return -EINVAL; r = (vol >> 8) & 0xff; if (r > 100) r = 100; l = vol & 0xff; if (l > 100) l = 100; hal2->mixer.volume[mixtable[index].idx] = l | (r << 8); switch (mixtable[index].idx) { case H2_MIX_OUTPUT_ATT: DEBUG_MIX("output attenuator %d,%d\n", l, r); if (r | l) { tmp = hal2_i_look32(hal2, H2I_DAC_C2); tmp &= ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE); /* Attenuator has five bits */ l = 31 * (100 - l) / 99; r = 31 * (100 - r) / 99; DEBUG_MIX("left: %d, right %d\n", l, r); tmp |= (l << H2I_C2_L_ATT_SHIFT) & H2I_C2_L_ATT_M; tmp |= (r << H2I_C2_R_ATT_SHIFT) & H2I_C2_R_ATT_M; hal2_i_write32(hal2, H2I_DAC_C2, tmp); } else hal2_i_setbit32(hal2, H2I_DAC_C2, H2I_C2_MUTE); break; case H2_MIX_INPUT_GAIN: DEBUG_MIX("input gain %d,%d\n", l, r); tmp = hal2_i_look32(hal2, H2I_ADC_C2); tmp &= ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M); /* Gain control has four bits */ l = 16 * l / 100; r = 16 * r / 100; DEBUG_MIX("left: %d, right %d\n", l, r); tmp |= (l << H2I_C2_L_GAIN_SHIFT) & H2I_C2_L_GAIN_M; tmp |= (r << H2I_C2_R_GAIN_SHIFT) & H2I_C2_R_GAIN_M; hal2_i_write32(hal2, H2I_ADC_C2, tmp); break; } return 0;}static void hal2_init_mixer(struct hal2_card *hal2){ int i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].avail) hal2->mixer.volume[mixtable[i].idx] = 100 | (100 << 8); /* disable attenuator */ hal2_i_write32(hal2, H2I_DAC_C2, 0); /* set max input gain */ hal2_i_write32(hal2, H2I_ADC_C2, H2I_C2_MUTE | (H2I_C2_L_GAIN_M << H2I_C2_L_GAIN_SHIFT) | (H2I_C2_R_GAIN_M << H2I_C2_R_GAIN_SHIFT)); /* set max volume */ hal2->mixer.master = 0xff; hal2->vol_regs->left = 0xff; hal2->vol_regs->right = 0xff;}/* * XXX: later i'll implement mixer for main volume which will be disabled * by default. enabling it users will be allowed to have master volume level * control on panel in their favourite X desktop */static void hal2_volume_control(int direction){ unsigned int master = hal2_card[0]->mixer.master; struct hal2_vol_regs *vol = hal2_card[0]->vol_regs; /* volume up */ if (direction > 0 && master < 0xff) master++; /* volume down */ else if (direction < 0 && master > 0) master--; /* TODO: mute/unmute */ vol->left = master; vol->right = master; hal2_card[0]->mixer.master = master;}static int hal2_mixer_ioctl(struct hal2_card *hal2, unsigned int cmd, unsigned long arg){ int val; if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, hal2str, sizeof(info.id)); strlcpy(info.name, hal2str, sizeof(info.name)); info.modify_counter = hal2->mixer.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, hal2str, sizeof(info.id)); strlcpy(info.name, hal2str, sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) return -EINVAL; if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { /* Give the current record source */ case SOUND_MIXER_RECSRC: val = 0; /* FIXME */ break; /* Give the supported mixers, all of them support stereo */ case SOUND_MIXER_DEVMASK: case SOUND_MIXER_STEREODEVS: { int i; for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].avail) val |= 1 << i; break; } /* Arg contains a bit for each supported recording source */ case SOUND_MIXER_RECMASK: val = 0; break; case SOUND_MIXER_CAPS: val = 0; break; /* Read a specific mixer */ default: { int i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) return -EINVAL; val = hal2->mixer.volume[mixtable[i].idx]; break; } } return put_user(val, (int *)arg); } if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) return -EINVAL; hal2->mixer.modcnt++; if (get_user(val, (int *)arg)) return -EFAULT; switch (_IOC_NR(cmd)) { /* Arg contains a bit for each recording source */ case SOUND_MIXER_RECSRC: return 0; /* FIXME */ default: return hal2_write_mixer(hal2, _IOC_NR(cmd), val); } return 0;}static int hal2_open_mixdev(struct inode *inode, struct file *file){ struct hal2_card *hal2 = hal2_mixer_find_card(iminor(inode)); if (hal2) { file->private_data = hal2; return nonseekable_open(inode, file); } return -ENODEV;}static int hal2_release_mixdev(struct inode *inode, struct file *file){ return 0;}static int hal2_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return hal2_mixer_ioctl((struct hal2_card *)file->private_data, cmd, arg);}static int hal2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int val; struct hal2_card *hal2 = (struct hal2_card *) file->private_data; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) return hal2_sync_dac(hal2); return 0; case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_GETCAPS: return put_user(DSP_CAP_DUPLEX | DSP_CAP_MULTI, (int *)arg); case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_READ) { hal2_stop_adc(hal2); hal2_reset_adc_pointer(hal2); } if (file->f_mode & FMODE_WRITE) { hal2_stop_dac(hal2); hal2_reset_dac_pointer(hal2); } return 0; case SNDCTL_DSP_SPEED: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { hal2_stop_adc(hal2); val = hal2_compute_rate(&hal2->adc, val); hal2->adc.sample_rate = val; hal2_set_adc_rate(hal2); } if (file->f_mode & FMODE_WRITE) { hal2_stop_dac(hal2); val = hal2_compute_rate(&hal2->dac, val); hal2->dac.sample_rate = val; hal2_set_dac_rate(hal2); } return put_user(val, (int *)arg); case SNDCTL_DSP_STEREO: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { hal2_stop_adc(hal2); hal2->adc.voices = (val) ? 2 : 1; hal2_setup_adc(hal2); } if (file->f_mode & FMODE_WRITE) { hal2_stop_dac(hal2); hal2->dac.voices = (val) ? 2 : 1; hal2_setup_dac(hal2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -