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

📄 mx27-wm8782-pcm.c.bak

📁 根据芯片WM8782在LINUX环境下实现的音频驱动
💻 BAK
📖 第 1 页 / 共 2 页
字号:
/*
 *  Copyright (c) sitek hengke elec .Ltd <ihanker.com>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 */
//#include <stdio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/spinlock_types.h>

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/arch/dma.h>

#include <asm/arch/mx27_pins.h>
#include <asm/arch/mx2_dma.h>
#include <asm/arch/clock.h>

#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>


#include <ssi/ssi.h>
#include <ssi/registers.h>
#include <ssi/ssi_types.h>

#include "wm8782.h"
#include "mxc-pcm.h"
#include "wm8782-mixer.h"


//#define TALK 5
#define debug 0
#if defined (TALK)
#define DBG(l,f...)  do{ if (l <= TALK) printk (f); }while(0)
#define ENTRY(l) DBG(l, "%s: entry\n", __FUNCTION__)
#else
#define ENTRY(l) do {} while (0)
#define DBG(l,f...) do {} while (0)
#endif

#define MAX_BUFFER_SIZE  		(32 * 1024)
#define DMA_BUF_SIZE		(8 * 1024)
#define MIN_PERIOD_SIZE		64
#define MIN_PERIOD		2
#define MAX_PERIOD		255


#define SSI_CODEC SSI1 // 0
extern void mx27_i2s_slave_mode_config(void);
extern void mx27_i2s_normal_mode_config(void);

static snd_pcm_hardware_t mx27_codec_playback_hw =
{
	.info		= (  SNDRV_PCM_INFO_INTERLEAVED 
			   | SNDRV_PCM_INFO_BLOCK_TRANSFER 
			   | SNDRV_PCM_INFO_MMAP
			   | SNDRV_PCM_INFO_MMAP_VALID 
//			   | sNDRV_PCM_INFO_PAUsE
//			   | sNDRV_PCM_INFO_REsUME
		),
	.formats	=    SNDRV_PCM_FMTBIT_S16_LE,
	.rates		= (0
			   | SNDRV_PCM_RATE_8000
			   | SNDRV_PCM_RATE_11025
			   | SNDRV_PCM_RATE_16000
			   | SNDRV_PCM_RATE_22050
			   | SNDRV_PCM_RATE_32000
			   | SNDRV_PCM_RATE_44100
			   | SNDRV_PCM_RATE_48000
			   | SNDRV_PCM_RATE_88200
			   | SNDRV_PCM_RATE_96000
			   | SNDRV_PCM_RATE_176400
			   | SNDRV_PCM_RATE_192000
		),
	.rate_min	= 8000,
	.rate_max	= 192000,
	.channels_min	= 1,
	.channels_max	= 2,
	.buffer_bytes_max = MAX_BUFFER_SIZE,
	.period_bytes_min = MIN_PERIOD_SIZE,
	.period_bytes_max = DMA_BUF_SIZE,
	.periods_min	= MIN_PERIOD,
	.periods_max	= MAX_PERIOD,
	.fifo_size	= 0,
};

static snd_pcm_hardware_t mx27_codec_capture_hw =
{
	.info		= (  SNDRV_PCM_INFO_INTERLEAVED 
			   | SNDRV_PCM_INFO_BLOCK_TRANSFER 
			   | SNDRV_PCM_INFO_MMAP
			   | SNDRV_PCM_INFO_MMAP_VALID 
//			   | SNDRV_PCM_INFO_PAUsE
//			   | SNDRV_PCM_INFO_REsUME
		),
	.formats	=    SNDRV_PCM_FMTBIT_S16_LE,
	.rates		= (0
			   | SNDRV_PCM_RATE_8000
			   | SNDRV_PCM_RATE_11025
			   | SNDRV_PCM_RATE_16000
			   | SNDRV_PCM_RATE_22050
			   | SNDRV_PCM_RATE_32000
			   | SNDRV_PCM_RATE_44100
			   | SNDRV_PCM_RATE_48000
			   | SNDRV_PCM_RATE_88200
			   | SNDRV_PCM_RATE_96000
			   | SNDRV_PCM_RATE_176400
			   | SNDRV_PCM_RATE_192000
		),
	.rate_min	= 8000,
	.rate_max	= 192000,
	.channels_min	= 1,
	.channels_max	= 2,
	.buffer_bytes_max = MAX_BUFFER_SIZE,
	.period_bytes_min = MIN_PERIOD_SIZE,
	.period_bytes_max = DMA_BUF_SIZE,
	.periods_min	= MIN_PERIOD,
	.periods_max	= MAX_PERIOD,
	.fifo_size	= 0,
};

static unsigned int rates[] = {
	8000, 11025, 16000,22050, 32000, 44100, 48000,88200,96000,176400,192000
};

static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
	.count = ARRAY_SIZE (rates),
	.list = rates,
	.mask = 0,
};

/* 
mx27_codec_init, called from the driver initialization function to initialize the ALsA structures.
*/
static void mx27_codec_init (mx27_codec_t* codec)
{
	ENTRY (0);

	/* setup DMA stuff */
	codec->s[SNDRV_PCM_STREAM_PLAYBACK].id = "wm8782 out";
	codec->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id
		= SNDRV_PCM_STREAM_PLAYBACK;

	codec->s[SNDRV_PCM_STREAM_CAPTURE].id = "wm8782 in";
	codec->s[SNDRV_PCM_STREAM_CAPTURE].stream_id
		= SNDRV_PCM_STREAM_CAPTURE;
}

/*!
  * This function configures the DMA channel used to transfer audio from MCU to wm9712
  *
  * @param	substream	pointer to the structure of the current stream.
  * @param	callback		pointer to function that will be called 
				when a sDMA TX transfer finishes.
  * @return              0 on success, -1 otherwise.
*/
static int configure_write_channel(audio_stream_t * s, mxc_dma_callback_t callback)
{
	int ret = -1;
	int channel = -1;

	ENTRY(0);

	channel = mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA");
	if (channel < 0) {
		printk("error requesting a write dma channel\n");
		return -1;
	}
	s->dma_channel = channel; 

	ret = mxc_dma_callback_set(s->dma_channel, 
			(mxc_dma_callback_t) callback,(void *)s);
	return ret;
}



//config for sitek.cn xtp-d501 board,using ssi1.
static int configure_read_channel(audio_stream_t * s, mxc_dma_callback_t callback)
{
	int ret = -1;
	int channel = -1;
	ENTRY (0);
	channel = mxc_dma_request(MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA");
	//need to check by zd
	
	if (channel < 0) {
		printk("error requesting a read dma channel\n");
		return -1;
	}
	s->dma_channel = channel;
	//printk("zd: configure_read_channel=%d\n", channel);
	ret = mxc_dma_callback_set(s->dma_channel, 
			(mxc_dma_callback_t) callback,(void *)s);
	return ret;
}

/*!
  * This function gets the dma pointer position during record.
  * Our DMA implementation does not allow to retrieve this position
  * when a transfert is active, so, it answers the middle of
  * the current period beeing transfered
  *
  * @param	s	pointer to the structure of the current stream.
  *
*/
static unsigned int audio_get_capture_dma_pos(audio_stream_t *s)
{
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int offset;

	substream = s->substream;
	runtime = substream->runtime;
	offset = 0;
	ENTRY (1);

	// printk("%s\n",__FUNCTION__);
#if 0
        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("buffer_size=%ld \n",substream->runtime->buffer_size);
	printk("s->tx_pin=%d \n",s->tx_spin);
	printk("s->periods=%d \n",s->periods);
	printk("s->period=%d \n",s->period);
	printk("s->dma_channel=%d \n",s->dma_channel);
	printk("runtime->period_size=%ld \n",runtime->period_size);
	//for debug
#endif
	/* tx_spin value is used here to check if a transfert is active */
	if (s->tx_spin) {
		offset = (runtime->period_size * (s->periods)) +
		    (runtime->period_size >> 1);
	//	printk("zd:MXC before: audio_get_dma_pos offset  %d\n", offset);
		if (offset >= runtime->buffer_size)
			offset = runtime->period_size >> 1;
		//printk("MXC: audio_get_dma_pos offset  %d\n", offset);
	} else {
		offset = (runtime->period_size * (s->periods));
	//	printk("zd:MXC after: audio_get_dma_pos offset  %d\n", offset);
		if (offset >= runtime->buffer_size)
			offset = 0;
		//printk("MXC: audio_get_dma_pos BIs offset  %d\n", offset);
	}

	return offset;
}

/*!
  * This function gets the dma pointer position during playback.
  * Our DMA implementation does not allow to retrieve this position
  * when a transfert is active, so, it answers the middle of
  * the current period beeing transfered
  *
  * @param	s	pointer to the structure of the current stream.
  *
*/
static unsigned int audio_get_playback_dma_pos(audio_stream_t * s)
{
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int offset;


	substream = s->substream;
	runtime = substream->runtime;
	offset = 0;

	/* tx_spin value is used here to check if a transfert is active */
	if (s->tx_spin) {
		offset = runtime->period_size * (s->periods);
		if (offset >= runtime->buffer_size)
			offset = 0;
		//DBG(1,"MXC: audio_get_dma_pos offset  %d\n", offset);
	} else {
		offset = (runtime->period_size * (s->periods));
		if (offset >= runtime->buffer_size)
			offset = 0;
	//	DBG(1,"MXC: audio_get_dma_pos BIs offset  %d\n", offset);
	}

	
	return offset;
}

/*!
  * This function stops the current dma transfert for playback
  * and clears the dma pointers.
  * @param	substream	pointer to the structure of the current stream.
*/
static void audio_playback_stop_dma(audio_stream_t * s)
{
	unsigned long flags;
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int offset;

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

	spin_lock_irqsave(&s->dma_lock, flags);

	DBG(1,"MXC : audio_stop_dma active = 0\n");
	s->active = 0;
	s->period = 0;
	s->periods = 0;

	/* this stops the dma channel and clears the buffer ptrs */
	mxc_dma_disable(s->dma_channel);
	
	dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
			 DMA_TO_DEVICE);

	spin_unlock_irqrestore(&s->dma_lock, flags);

}

/*!
  * This function stops the current dma transfer for capture 
  * and clears the dma pointers.
  * @param	substream	pointer to the structure of the current stream.
*/
static void audio_capture_stop_dma(audio_stream_t * s)
{
	unsigned long flags;
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int offset;
	
	ENTRY (0);
	
	substream = s->substream;
	runtime = substream->runtime;
	dma_size = frames_to_bytes(runtime, runtime->period_size);
	offset = dma_size * s->periods;

	spin_lock_irqsave(&s->dma_lock, flags);

	DBG(1,"MXC : audio_stop_dma active = 0\n");
	s->active = 0;
	s->period = 0;
	s->periods = 0;

	/* this stops the dma channel and clears the buffer ptrs */
	mxc_dma_disable(s->dma_channel);
	dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
			 DMA_FROM_DEVICE);

	spin_unlock_irqrestore(&s->dma_lock, flags);

}

/*!
  * This function is called whenever a new audio block needs to be
  * transferred to wm9712. The function receives the address and the size
  * of the new block and start a new DMA transfer.
  *
  * @param	substream	pointer to the structure of the current stream.
  *
*/
static void audio_playback_dma(audio_stream_t * s)
{
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int offset;
	int ret = 0;
	mxc_dma_requestbuf_t dma_request;
	
	substream = s->substream;
	runtime = substream->runtime;
	DBG(1,"\nDMA  is playback\n");

	memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));

	if (s->active) {
		dma_size = frames_to_bytes(runtime, runtime->period_size);
		DBG(1,"s->period (%x) runtime->periods (%d)\n",
			  s->period, runtime->periods);
		DBG(1,"runtime->period_size (%d) dma_bytes (%d)\n",
			  (unsigned int)runtime->period_size,
			  runtime->dma_bytes);

		offset = dma_size * s->period;
		snd_assert(dma_size <= DMA_BUF_SIZE,);

		dma_request.src_addr = (dma_addr_t) (dma_map_single(NULL,
							runtime->dma_area+ offset,
							dma_size,
							DMA_TO_DEVICE));
		
		dma_request.dst_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1STX0);
		dma_request.num_of_bytes = dma_size;

		DBG(1,"MXC: start DMA offset=%d size=%d\n", offset,runtime->dma_bytes);
		DBG(1,"dst=%x,src=%x\n", dma_request.dst_addr,dma_request.src_addr);

		mxc_dma_config(s->dma_channel, &dma_request, 1,
			       MXC_DMA_MODE_WRITE);
		
		ret = mxc_dma_enable(s->dma_channel);
		//ssi_transmit_enable(SSI_CODEC, 1);	
		//printk("dma_size is 0x%x\n",dma_size);
		
		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;
	}

}

/*!
  * This function is called whenever a new audio block needs to be
  * transferred from wm8782. The function receives the address and the size
  * of the block that will store the audio samples and start a new DMA transfer.
  *
  * @param	substream	pointer to the structure of the current stream.
  *
*/
static void audio_capture_dma(audio_stream_t * s)
{
	snd_pcm_substream_t *substream;
	snd_pcm_runtime_t *runtime;
	unsigned int dma_size;
	unsigned int offset;
	int ret = 0;
	mxc_dma_requestbuf_t dma_request;
	
	//ENTRY (0);
//	printk("zd:audio_capture_dma entry\n");
	
	substream = s->substream;
	runtime = substream->runtime;

	memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));

	if (s->active) {
		dma_size = frames_to_bytes(runtime, runtime->period_size);
	//	printk("s->period (%x) runtime->periods (%d)\n",
	//		  s->period, runtime->periods);
	//	printk("runtime->frame_bit=%d\n", runtime->frame_bits);
	//	printk("runtime->period_size (%d) dma_size (%d)\n",
	//		  (unsigned int)runtime->period_size,runtime->dma_bytes);

		offset = dma_size * s->period;
		snd_assert(dma_size <= DMA_BUF_SIZE,);

		dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL,
							runtime->dma_area + offset,
							dma_size,
							DMA_FROM_DEVICE));
		
		dma_request.src_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1SRX0);
		
		dma_request.num_of_bytes = dma_size;

		mxc_dma_config(s->dma_channel, &dma_request, 1,
			       MXC_DMA_MODE_READ);
		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;

⌨️ 快捷键说明

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