⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hal2.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	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 + -