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

📄 linux-at91rm9200-i2s.c

📁 基于at91rm9200-arm920t下的录放音程序
💻 C
📖 第 1 页 / 共 5 页
字号:
			s->buf_idx++;
			if(s->buf_idx>=s->nbfrags)
				s->buf_idx = 0;
			s->buf = s->buffers+s->buf_idx;
		}
			
		if(b->size==0)
			up(&(b->sem));
		b->size = s->fragsize;
	}
	return;//IRQ_HANDLED;
}
*/
static void iis_dma_done_handler(int irq, void *dev_id, struct pt_regs *regs)
{
	AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
	audio_stream_t *s = &output_stream;
	
//	printk("i");

	if(s->buffers) {
		audio_buf_t *b = s->buffers+wr_buf_tail;
		
		wr_buf_tail++;
		if(wr_buf_tail>=s->nbfrags)
			wr_buf_tail = 0;
		
		up(&b->sem);
//		wake_up(&b->sem.wait);

		b = s->buffers+wr_buf_tail;
		if((b!=s->buf)&&(b->dma_size)) {
			pSSC->SSC_TPR  = b->dma_addr;
			pSSC->SSC_TCR  = b->dma_size>>1;
			b->dma_size = 0;
		//		printk(KERN_ERR "%d\n", wr_buf_tail);
		} else {
			play_dma_running = 0;			
		}
	} else {
		printk(KERN_DEBUG "buffers is null\n");
		//pSSC->SSC_PTCR = (1<<1)|(1<<9);	//disable PDC tx and rx
		//pSSC->SSC_CR   = (1<<1)|(1<<9);	//disable tx and rx
	}

//	disable_irq(AT91C_ID_SSC2);
//	((AT91PS_SYS)AT91C_VA_BASE_SYS)->AIC_ICCR = 1<<AT91C_ID_SSC2;

	return;//IRQ_HANDLED;
}

 /* using when write */
static int audio_sync(struct file *file)
{
	audio_stream_t *s = &output_stream;
	audio_buf_t *b = s->buf;
	unsigned long flags;

	DPRINTK("audio_sync\n");

	if (!s->buffers)
		return 0;

	if (b->size != 0) {
		down(&b->sem);
		//s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
		//			 b->dma_addr, b->size, DMA_BUF_WR);
		
		local_irq_save(flags);
		if(!play_dma_running) {
			AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
			
			play_dma_running = 1;
			pSSC->SSC_TPR  = b->dma_addr;
			pSSC->SSC_TCR  = b->size>>1;
			pSSC->SSC_CR   = 1<<8;
			pSSC->SSC_PTCR = 1<<8;
		} else
			b->dma_size = b->size;					
		b->size = 0;
		NEXT_BUF(s, buf);
		local_irq_restore(flags);
	}

	b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);
	if (down_interruptible(&b->sem))
		return -EINTR;
	up(&b->sem);

	return 0;
}

static inline int copy_from_user_mono_stereo(char *to, const char *from, int count)
{
	u_int *dst = (u_int *)to;
	const char *end = from + count;

	if (verify_area(VERIFY_READ, from, count))
		return -EFAULT;

	if ((int)from & 0x2) {
		u_int v;
		__get_user(v, (const u_short *)from); from += 2;
		*dst++ = v | (v << 16);
	}

	while (from < end-2) {
		u_int v, x, y;
		__get_user(v, (const u_int *)from); from += 4;
		x = v << 16;
		x |= x >> 16;
		y = v >> 16;
		y |= y << 16;
		*dst++ = x;
		*dst++ = y;
	}

	if (from < end) {
		u_int v;
		__get_user(v, (const u_short *)from);
		*dst = v | (v << 16);
	}

	return 0;
}

static ssize_t smdk2410_audio_write(struct file *file, const char *buffer, 
				    size_t count, loff_t * ppos)
{
	const char *buffer0 = buffer;
	audio_stream_t *s = &output_stream;
	int chunksize, ret = 0;
	unsigned long flags;

	DPRINTK("audio_write : start count=%d\n", count);

	switch (file->f_flags & O_ACCMODE) {
	  	case O_WRONLY:
	  	case O_RDWR:
			break;
	  	default:
		  	return -EPERM;
	}

	if (!s->buffers && audio_setup_buf(s))
		return -ENOMEM;

	count &= ~0x03;

	while (count > 0) {
		audio_buf_t *b = s->buf;

		if (file->f_flags & O_NONBLOCK) {
			ret = -EAGAIN;
			if (down_trylock(&b->sem))
				break;
		} else {
			ret = -ERESTARTSYS;
			if (down_interruptible(&b->sem))
				break;
		}

		if (audio_channels == 2) {
			chunksize = s->fragsize - b->size;
			if (chunksize > count)
				chunksize = count;
			DPRINTK("write %d to %d\n", chunksize, s->buf_idx);
			if (copy_from_user(b->start + b->size, buffer, chunksize)) {
				up(&b->sem);
				return -EFAULT;
			}
			b->size += chunksize;
		} else {
			chunksize = (s->fragsize - b->size) >> 1;

			if (chunksize > count)
				chunksize = count;
			DPRINTK("write %d to %d\n", chunksize*2, s->buf_idx);
			if (copy_from_user_mono_stereo(b->start + b->size, 
				    		       buffer, chunksize)) {
				up(&b->sem);
				return -EFAULT;
			}

			b->size += chunksize*2;
		}

		buffer += chunksize;
		count -= chunksize;
		if (b->size < s->fragsize) {
			up(&b->sem);
			break;
		}

		//s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
		//			   b->dma_addr, b->size, DMA_BUF_WR);
		
		local_irq_save(flags);
		if(!play_dma_running) {
			AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
			
			play_dma_running = 1;
			pSSC->SSC_TPR  = b->dma_addr;
			pSSC->SSC_TCR  = b->size>>1;
			pSSC->SSC_CR   = 1<<8;
			pSSC->SSC_PTCR = 1<<8;
		} else
			b->dma_size = b->size;
		b->size = 0;
		NEXT_BUF(s, buf);
		local_irq_restore(flags);
	}

	if ((buffer - buffer0))
		ret = buffer - buffer0;

	DPRINTK("audio_write : end count=%d\n\n", ret);

	return ret;
}

static ssize_t smdk2410_audio_read(struct file *file, char *buffer, 
                                        size_t count, loff_t * ppos)
{
	const char *buffer0 = buffer;
	audio_stream_t *s = &input_stream;
	int chunksize, ret = 0;

	DPRINTK("audio_read: count=%d\n", count);

	if (ppos != &file->f_pos)
		return -ESPIPE;

	if (!s->buffers) {
		AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
		int i;
 
		if (audio_setup_buf(s))
			return -ENOMEM;
 
		for (i = 0; i < s->nbfrags; i++) {
			audio_buf_t *b = s->buf;
			down(&b->sem);
			NEXT_BUF(s, buf);
		}

		pSSC->SSC_RPR  = s->buf->dma_addr;
		pSSC->SSC_RCR  = s->fragsize>>1;
		pSSC->SSC_CR   = 1<<0;
		pSSC->SSC_PTCR = 1<<0;
	}
	
	while (count > 0) {
		audio_buf_t *b = s->buf;

		/* Wait for a buffer to become full */
		if (file->f_flags & O_NONBLOCK) {
			ret = -EAGAIN;
			if (down_trylock(&b->sem))
				break;
		} else {
			ret = -ERESTARTSYS;
			if (down_interruptible(&b->sem))
				break;
		}

		chunksize = b->size;
		if (chunksize > count)
			chunksize = count;
		DPRINTK("read %d from %d\n", chunksize, s->buf_idx);
		if (copy_to_user(buffer, b->start + s->fragsize - b->size, chunksize)) {
			up(&b->sem);
			return -EFAULT;
		}
		
		b->size -= chunksize;
		buffer += chunksize;
		count -= chunksize;
		if (b->size > 0) {
			up(&b->sem);
			break;
		}
		NEXT_BUF(s, buf);
	}
	
	if ((buffer - buffer0))
		ret = buffer - buffer0;

	DPRINTK("audio_read: return=%d\n", ret);

	return ret;
}

static unsigned int smdk2410_audio_poll(struct file *file, 
					struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	int i;

	DPRINTK("audio_poll(): mode=%s\n",
		(file->f_mode & FMODE_WRITE) ? "w" : "");

	if (file->f_mode & FMODE_READ) {
                if (!input_stream.buffers && audio_setup_buf(&input_stream))
                        return -ENOMEM;
                poll_wait(file, &input_stream.buf->sem.wait, wait);

                for (i = 0; i < input_stream.nbfrags; i++) {
                        if (atomic_read(&input_stream.buffers[i].sem.count) > 0)
                                mask |= POLLIN | POLLWRNORM;
				break;
                }
        }

	if (file->f_mode & FMODE_WRITE) {
		if (!output_stream.buffers && audio_setup_buf(&output_stream))
			return -ENOMEM;
		poll_wait(file, &output_stream.buf->sem.wait, wait);

		for (i = 0; i < output_stream.nbfrags; i++) {
			if (atomic_read(&output_stream.buffers[i].sem.count) > 0)
				mask |= POLLOUT | POLLWRNORM;
				break;
		}
	}

	DPRINTK("audio_poll() returned mask of %s\n",
		(mask & POLLOUT) ? "w" : "");

	return mask;
}

static loff_t smdk2410_audio_llseek(struct file *file, loff_t offset, 
				    int origin)
{
            return -ESPIPE;
}

static int audio_set_dsp_speed(__u32 val)
{
	AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
	AT91PS_TCB pTCB = (AT91PS_TCB)AT91C_VA_BASE_TCB0;
	AT91PS_TC  pTC  = (AT91PS_TC)&pTCB->TCB_TC1;
	int i;
	
	switch (val) {
	case 8000:
	case 11025:
	case 16000:
	case 22050:
	case 24000:
	case 32000:
	case 44100:
	case 48000:
		break;
	default:
		printk("Invalid value\n");
		return -1;
	}

	for(i=0; i<ARRAY_SIZE(iis_psr_para); i++) {		
		if(iis_psr_para[i].rate==val)
			break;
	}
	if(i>=ARRAY_SIZE(iis_psr_para)) {
		printk("Invalid psr parameters\n");
		return -1;
	}	
	
	pSSC->SSC_CMR = iis_psr_para[i].psr;
//	pTC->TC_CCR = 2;	//disable tc
	pTC->TC_IDR = 0xff;	//disable all interrupts
	//select TIMER_CLOCK1 = MCK/2, CPCTRG, up mode, Waveform mode
	pTC->TC_CMR = 0|(2<<13)|(1<<15)|(1<<16)|(2<<18);	//Capture模式下,RC COMPARE也可复位计数器	
	
	uda1341_l3_address(UDA1341_REG_STATUS);
	if(iis_psr_para[i].fs==384) {
		uda1341_l3_data(STAT0_SC_384FS | STAT0_IF_MSB);     // set 384 system clock, MSB
		pTC->TC_RC  = iis_psr_para[i].psr/12;	//1/12
	} else if(iis_psr_para[i].fs==256) {
		uda1341_l3_data(STAT0_SC_256FS | STAT0_IF_MSB);     // set 256 system clock, MSB
		pTC->TC_RC  = iis_psr_para[i].psr>>3;	//1/8
	} else {
		uda1341_l3_data(STAT0_SC_256FS | STAT0_IF_MSB);     // set 128 system clock, MSB
		pTC->TC_RC  = iis_psr_para[i].psr>>2;	//1/4
	}
	pTC->TC_RA  = pTC->TC_RC>>1;
	pTC->TC_CCR = 5;	//enable timer-counter and trig it
	DPRINTK("RC_VAL=0x%x\n", pTC->TC_RC);
	
	i = val;
	return i;
}

static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file, 
                                unsigned int cmd, unsigned long arg)
{
	static __u32 audio_mix_modcnt = 0;
	int ret;
	long val = 0;
	
	audio_mix_modcnt++;

	switch (cmd) {
		case SOUND_MIXER_INFO:
		{
			mixer_info info;
			memset(&info, 0, sizeof(info));
			strncpy(info.id, "UDA1341", sizeof(info.id));
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
			info.modify_counter = audio_mix_modcnt;
			return copy_to_user((void *)arg, &info, sizeof(info));
		}
		case SOUND_OLD_MIXER_INFO:
		{
			_old_mixer_info info;
			memset(&info, 0, sizeof(info));
			strncpy(info.id, "UDA1341", sizeof(info.id));
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
			return copy_to_user((void *)arg, &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_WRITE_VOLUME:
			ret = get_user(val, (long *) arg);
			if (ret)
				return ret;
			uda1341_volume = 63 - (((val & 0xff) + 1) * 63) / 100;
			uda1341_l3_address(UDA1341_REG_DATA0);
			uda1341_l3_data(uda1341_volume);
			break;
		
		case SOUND_MIXER_READ_VOLUME:
			val = ((63 - uda1341_volume) * 100) / 63;
			val |= val << 8;
			return put_user(val, (long *) arg);
	
		case SOUND_MIXER_READ_IGAIN:
			val = ((31- mixer_igain) * 100) / 31;
			return put_user(val, (int *) arg);

		case SOUND_MIXER_WRITE_IGAIN:
			ret = get_user(val, (int *) arg);
			if (ret)
				return ret;
			mixer_igain = 31 - (val * 31 / 100);		
			/* use mixer gain channel 1*/
			uda1341_l3_address(UDA1341_REG_DATA0);
			uda1341_l3_data(EXTADDR(EXT0));
			uda1341_l3_data(EXTDATA(EXT0_CH1_GAIN(mixer_igain)));			
			break;

		default:
			DPRINTK("mixer ioctl %u unknown\n", cmd);
			return -ENOSYS;
	}
	
	return 0;
}

static int smdk2410_audio_ioctl(struct inode *inode, struct file *file, 
                                uint cmd, ulong arg)
{
	long val;

	switch (cmd) {
		case OSS_GETVERSION:
		return put_user(SOUND_VERSION, (int *)arg);
		
	  	case SNDCTL_DSP_SETFMT:
			get_user(val, (long *) arg);
		  	if (val & AUDIO_FMT_MASK) {
			    	audio_fmt = val;
			    	break;
		  	} else
				return -EINVAL;

	  	case SNDCTL_DSP_CHANNELS:
	  	case SNDCTL_DSP_STEREO:
		  	get_user(val, (long *) arg);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -