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

📄 mx27-wm8782-pcm.c

📁 根据芯片WM8782在LINUX环境下实现的音频驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
		ret = mxc_dma_enable(s->dma_channel);

		s->tx_spin = 1;	/* FGA little trick to retrieve DMA pos */

		if (ret) {
			printk("audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
			return;
		}
		s->period++;
		s->period %= runtime->periods;
	}
	
}

static void audio_playback_dma_callback(void *data, int error,
					unsigned int count)
{
	audio_stream_t *s;
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int previous_period;
	unsigned int offset;

	ENTRY(0);

	s = data;
	substream = s->substream;
	runtime = substream->runtime;
	previous_period = s->periods;
	dma_size = frames_to_bytes(runtime, runtime->period_size);
	offset = dma_size * previous_period;

	s->tx_spin = 0;
	s->periods++;
	s->periods %= runtime->periods;

	/*
	 * Give back to the CPU the access to the non cached memory
	 */
	dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
			 DMA_TO_DEVICE);

	/*
	 * If we are getting a callback for an active stream then we inform
	 * the PCM middle layer we've finished a period
	 */
	if (s->active)
		snd_pcm_period_elapsed(s->substream);

	spin_lock(&s->dma_lock);

	/*
	 * Trig next DMA transfer
	 */
	audio_playback_dma(s);

	spin_unlock(&s->dma_lock);

}
/*!
  * This is a callback which will be called when a RX transfer finishes. 
  * The call occurs in interrupt context.
  * @param	substream	pointer to the structure of the current stream.
  */
static void audio_capture_dma_callback(void *data, int error,
				       unsigned int count)
{
	audio_stream_t *s;
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int previous_period;
	unsigned int offset;

	s = data;
	substream = s->substream;
	runtime = substream->runtime;
	previous_period = s->periods;
	dma_size = frames_to_bytes(runtime, runtime->period_size);
	offset = dma_size * previous_period;
	
	s->tx_spin = 0;
	s->periods++;
	s->periods %= runtime->periods;

	/*
	 * Give back to the CPU the access to the non cached memory
	 */
	dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
			 DMA_FROM_DEVICE);

	/*
	 * If we are getting a callback for an active stream then we inform
	 * the PCM middle layer we've finished a period
	 */
	if (s->active)
		snd_pcm_period_elapsed(s->substream);

	spin_lock(&s->dma_lock);

	/*
	 * Trig next DMA transfer
	 */
	audio_capture_dma(s);

	spin_unlock(&s->dma_lock);

}


static int wm8782_pcm_prepare(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime * runtime = substream->runtime;
	int rate = runtime->rate;

	printk(KERN_INFO "%s\n",__FUNCTION__);
	printk("rate=%d\n", rate);

	return 0;
}



static int mx27_codec_open(snd_pcm_substream_t * substream)
{
	mx27_codec_t* codec = snd_pcm_substream_chip (substream);
	snd_pcm_runtime_t* runtime = substream->runtime;
	int stream_id = substream->pstr->stream;
	int result;
	int err;

	ENTRY (0); 

	codec->s[stream_id].substream = substream;

	if (stream_id == SNDRV_PCM_STREAM_PLAYBACK){
		runtime->hw = mx27_codec_playback_hw;
	   	if ((err =
		configure_write_channel(&codec->s[SNDRV_PCM_STREAM_PLAYBACK],
				     audio_playback_dma_callback)) < 0)
		return err;

	}

	else if(stream_id == SNDRV_PCM_STREAM_CAPTURE){
		runtime->hw = mx27_codec_capture_hw;
		if ((err =
		configure_read_channel(&codec->s[SNDRV_PCM_STREAM_CAPTURE],
				     audio_capture_dma_callback)) < 0){		     
			return err;
			}
	}	   
	else{
	  	return -ENODEV;
		}
	result = snd_pcm_hw_constraint_integer(runtime,SNDRV_PCM_HW_PARAM_PERIODS);
	if (result < 0)
		return result;
	result = snd_pcm_hw_constraint_list(runtime, 0, 
					     SNDRV_PCM_HW_PARAM_RATE,
					     &hw_constraints_rates);
	return result < 0 ? result : 0;
}

static int mx27_codec_close (snd_pcm_substream_t * substream)
{
	mx27_codec_t* codec = snd_pcm_substream_chip (substream);
	int stream_id = substream->pstr->stream;
	int channel=-1;
	int result ;

	ENTRY(1);
	codec->s[substream->pstr->stream].substream = NULL;
	channel=codec->s[substream->pstr->stream].dma_channel;

	result=mxc_dma_free(channel);

	if (stream_id == SNDRV_PCM_STREAM_PLAYBACK){
		ssi_interrupt_disable(SSI_CODEC, ssi_tx_dma_interrupt_enable);
		ssi_interrupt_disable(SSI_CODEC, ssi_tx_fifo_0_empty);
		ssi_transmit_enable(SSI_CODEC, 0);
		ssi_tx_flush_fifo(SSI_CODEC);		
		}
	if (stream_id == SNDRV_PCM_STREAM_CAPTURE){
		ssi_interrupt_disable(SSI_CODEC, ssi_rx_dma_interrupt_enable);
		ssi_interrupt_disable(SSI_CODEC, ssi_rx_fifo_0_full);
		ssi_receive_enable(SSI_CODEC,0);
		ssi_rx_flush_fifo(SSI_CODEC);
		
		}
	return result < 0 ?result:0;
}

static int mx27_codec_hw_params (snd_pcm_substream_t* substream,
				     snd_pcm_hw_params_t* hw_params)
{
	snd_pcm_runtime_t *runtime;
	int result;

	ENTRY (0);

	runtime=substream->runtime;

	printk("dma_addr=%x\n",substream->runtime->dma_addr);
	   
	result = snd_pcm_lib_malloc_pages (substream, 
					   params_buffer_bytes (hw_params));
	if(result<0)
		return result;
	
	runtime->dma_addr = virt_to_phys(runtime->dma_area);
	
	DBG(1,"%s: dma_area=%x ",__FUNCTION__, (int)substream->runtime->dma_area);
	DBG(1,"dma_addr=%x,dma_bytes=%d\n",
		substream->runtime->dma_addr,substream->runtime->dma_bytes);
		
	return result;
}

static int mx27_codec_hw_free (snd_pcm_substream_t* substream)
{
	ENTRY (0);
	return snd_pcm_lib_free_pages (substream);
}

//start the codec.
static int mx27_codec_trigger(snd_pcm_substream_t* substream, int cmd)
{
	mx27_codec_t* codec = snd_pcm_substream_chip (substream);
	 int stream_id = substream->pstr->stream;
	audio_stream_t* s= &codec->s[stream_id];

	int result = 0;
       ENTRY (0);

#if 0
	printk("%s\n",__FUNCTION__);
        printk("rate=%d \n",substream->runtime->rate);
        printk("channel=%d \n",substream->runtime->channels);
        printk("frame_bits=%d \n",substream->runtime->frame_bits);
        printk("sample_bits=%d \n",substream->runtime->sample_bits);
        printk("periods=%d \n",substream->runtime->periods);
        printk("period_size=%ld \n",substream->runtime->period_size);
		
	printk("s->period=%d\n",s->period);
	printk("s->periods=%d\n",s->periods);
	printk("s->active=%d\n",s->active);
	printk("s->dma_channel=%d\n",s->dma_channel);
	printk("s->tx_spin=%d\n",s->tx_spin);
	printk("s->active=%d\n",s->active);
#endif

	DBG(0, "%s: trigger id %d cmd %d\n", __FUNCTION__, stream_id, cmd);

	if(stream_id==0)
		switch (cmd) {

		case SNDRV_PCM_TRIGGER_START:
			DBG (1, "trigger start stream %d\n", stream_id);

			s->tx_spin = 0;
			s->active = 1;

			audio_playback_dma(s);
			break;
		case SNDRV_PCM_TRIGGER_STOP:
			DBG (1, "trigger stop entry\n");
			s->tx_spin=0;
			s->active=0;
			audio_playback_stop_dma(s);
			break;
		case SNDRV_PCM_TRIGGER_SUSPEND:
			break;
		case SNDRV_PCM_TRIGGER_RESUME:
			break;
		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
			break;
		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
			break;
		default:
			result = -EINVAL;
			break;
	}

	if(stream_id==1)
		switch (cmd) {
		case SNDRV_PCM_TRIGGER_START:
		
			DBG (1, "trigger start stream %d\n", stream_id);
	  		s->tx_spin = 0;
			s->active = 1;
			audio_capture_dma(s);
	  		break;		
		case SNDRV_PCM_TRIGGER_STOP:
			DBG (1, "trigger stop\n");
			s->tx_spin=0;
			s->active=0;
			audio_capture_stop_dma(s);
			break;
		case SNDRV_PCM_TRIGGER_SUSPEND:
			break;
		case SNDRV_PCM_TRIGGER_RESUME:
			break;
		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
			break;
		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
			break;
		default:
			result = -EINVAL;
			break;
	}
	return result;
}

static int mx27_codec_prepare (snd_pcm_substream_t* substream)
{
	mx27_codec_t* codec = snd_pcm_substream_chip (substream);
	snd_pcm_runtime_t* runtime = substream->runtime;
	audio_stream_t* s = &codec->s[substream->pstr->stream];
	int id = substream->pstr->stream;
	//int channel = substream->runtime->channels;
	
	ENTRY (0);
	
	s->period = 0;
	s->periods = 0;	

	wm8782_pcm_prepare(substream);

	if(id==0){
        printk("it's playback!\n");
        }
	if(id==1){

		ssi_receive_enable(SSI_CODEC, 0);
      	
		mx27_i2s_master_mode_config();
		printk("rate=%d\n", runtime->rate);
		
		ssi_interrupt_enable(SSI_CODEC, ssi_rx_dma_interrupt_enable);
		ssi_interrupt_enable(SSI_CODEC, ssi_rx_fifo_0_full);
		ssi_receive_enable(SSI_CODEC,1);
	}		
	return 0;
}

static snd_pcm_uframes_t mx27_codec_pointer (snd_pcm_substream_t* substream)
{
	mx27_codec_t* codec = snd_pcm_substream_chip (substream);
	audio_stream_t* s = (audio_stream_t *)(&(codec->s[substream->pstr->stream]));
	int stream_id=substream->pstr->stream;
	unsigned int c=0;
	
	ENTRY (3);
	if(stream_id==0){
		c = audio_get_playback_dma_pos(s);
		DBG (3,"ID0 pos c=0x%x\n", c);		
      	}

	if(stream_id==1){
		c = audio_get_capture_dma_pos(s);
		DBG(3,"ID1 pos c=%x\n", c);
	}	
	
	return c;
}

static snd_pcm_ops_t mx27_codec_playback_ops = {
	.open		= mx27_codec_open,
	.close		= mx27_codec_close,
	.ioctl			= snd_pcm_lib_ioctl,
	.hw_params	= mx27_codec_hw_params,
	.hw_free		= mx27_codec_hw_free,
	.prepare		= mx27_codec_prepare,
	.trigger		= mx27_codec_trigger,
	.pointer		= mx27_codec_pointer,
};
static snd_pcm_ops_t mx27_codec_capture_ops = {
	.open		= mx27_codec_open,
	.close		= mx27_codec_close,
	.ioctl			= snd_pcm_lib_ioctl,
	.hw_params	= mx27_codec_hw_params,
	.hw_free		= mx27_codec_hw_free,
	.prepare		= mx27_codec_prepare,
	.trigger		= mx27_codec_trigger,
	.pointer		= mx27_codec_pointer,
};

/* mx27_codec_pcm_init
   performs the hardware initialization for the PCM device.
*/
 int  mx27_codec_pcm_init (mx27_codec_t* codec, int device)
{
	snd_pcm_t* pcm;
	int result = 0;

	ENTRY (0);

	result = snd_pcm_new (codec->card, 
			      "mx27 PCM", device, 1, 1, &pcm);
	if (result)
		goto done;

	snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK, 
			 &mx27_codec_playback_ops);
	snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE, 
			 &mx27_codec_capture_ops);

	snd_pcm_lib_preallocate_pages_for_all (pcm, 
				SNDRV_DMA_TYPE_CONTINUOUS,
				//((struct device *)(unsigned long)(GFP_KERNEL)),
				snd_dma_continuous_data(GFP_KERNEL),
				MAX_BUFFER_SIZE * 2 , MAX_BUFFER_SIZE * 2 );

	pcm->private_data = codec;
	pcm->info_flags = 0;
	strcpy (pcm->name, "mx27 PCM");

	mx27_codec_init (codec);
// audio_stream  init
	codec->pcm = pcm;

 done:
	return result;
}

EXPORT_SYMBOL_GPL(mx27_codec_pcm_init);

static int __init mx27_codec_driver_init (void)
{
	printk(KERN_INFO "MXC-WM8782 PCM module loaded successfully\n");
	return 0;
	
}

static void __exit mx27_codec_driver_exit (void)
{
	printk(KERN_INFO "MXC-WM8782 PCM module unloaded successfully\n");
}


module_init(mx27_codec_driver_init);
module_exit(mx27_codec_driver_exit);

MODULE_AUTHOR("Sitek Hengke, Ltd");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("xtp-d501 driver for ALSA");

⌨️ 快捷键说明

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