[SoC] DMA not generate interrupt !

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi

I am writing my audio driver for my board: TMPA910 CPU and WM8960 codec.
when I run pcm_min.c, there is an error: "Input/Output error".
I find that error because no DMA interrupt appear, printk() in DMA
interrupt program no appear

I have already set bit IE and ITC in DMA Configuaration register but no change.

Here is my source code.
Any idea, what could be the reason ?
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>

#include <asm/bug.h>
#include <asm/dma.h>
#include <asm/cacheflush.h>
#include <asm/arch/dma.h>
#include <asm/arch/tmpa910-regs.h>

#include "tmpa910_i2s.h"

#define TMPA910_I2S_DEBUG

#ifdef  TMPA910_I2S_DEBUG
#define i2s_printd(level, format, arg...) \
		printk(level "i2s: " format, ## arg)
#define I2S_ASSERT(expr) \
	do { \
		if (unlikely(!(expr))) { \
			printk(KERN_ERR "%s: %d, bug\n", __FUNCTION__, __LINE__); \
		} \
	} while(0)
#else
#define i2s_printd(level, format, arg...)
#define I2S_ASSERT(expr)
#endif

int tmpa910_i2s_config_tx(struct tmpa910_i2s *i2s)
{
	return 0;
}

static void setup_tx_desc(struct scatter_dma_t *desc, unsigned int phydesc, 
            unsigned int phy_buf, int fragcount, size_t fragsize)
{
	int i;

	for (i=0; i<fragcount; i++)
	{
		desc[i].lli = (unsigned long)(phydesc + (i + 1) * sizeof(struct scatter_dma_t));
		desc[i].srcaddr = (unsigned long)(phy_buf + i*fragsize);    //Phy Addr
		desc[i].dstaddr = (unsigned long)I2STDAT_ADR;
		desc[i].control = 0x84492000 + (unsigned long)(fragsize >> 2);
	}

	/* make circular */
	desc[fragcount-1].lli = (unsigned long)phydesc;
	
	printk("\nphydesc= %x\nphy_buff=%x\nragcount=%x\nfragsize=%x\n",phydesc,phy_buf,fragcount,fragsize);
}

static int i2s_start(struct tmpa910_i2s *i2s)
{
	int i;
	tmpa910_dma_enable(i2s->dma_tx_ch);

    /* I2S DMA set complete */
	I2STDMA1 = 0x0001;
    /* I2S transfer start */
	I2STSLVON = 0x0001;
	
	//for(i=0;i<=10000;i++)
	//{
	//	printk("\nI2STST = %d\n",I2STST);
	//	printk("\nI2SRST = %d\n",I2SRST);
	//}
	
	
	printk("====> Start I2S\n");
	return 0;
}

static int i2s_stop(struct tmpa910_i2s *i2s)
{
	I2STDMA1 = 0x0000;
	I2STSLVON = 0x0000;

	tmpa910_dma_disable(i2s->dma_tx_ch);

	printk("====> Stop I2S\n");

	return 0;
}

static inline int i2s_tx_dma_start(struct tmpa910_i2s *i2s)
{
    int dma_ch = i2s->dma_tx_ch;
	struct scatter_dma_t *dma_desc;

	i2s->curr_tx_desc = i2s->dma_tx_desc;
	dma_desc = i2s->curr_tx_desc;

    DMA_SRC_ADDR(dma_ch) = dma_desc->srcaddr;
	DMA_DEST_ADDR(dma_ch) = dma_desc->dstaddr;
	DMA_LLI(dma_ch) = dma_desc->lli;
	DMA_CONTROL(dma_ch) = dma_desc->control;
	//DMA_CONFIG(dma_ch) = 0x00008a81;
	DMA_CONFIG(dma_ch) = 0x0000Ca81;
	
	printk("\nstart dma: srcaddr = %x",DMA_SRC_ADDR(dma_ch));
	printk("\nstart dma: dstaddr = %x",DMA_DEST_ADDR(dma_ch));
	printk("\nstart dma: lli = %x",DMA_LLI(dma_ch));
	printk("\nstart dma: control = %x",DMA_CONTROL(dma_ch));
	printk("\nstart dma: config = %x\n",DMA_CONFIG(dma_ch));

	return 0;
}

int tmpa910_i2s_tx_start(struct tmpa910_i2s *i2s)
{
	
	i2s_printd(KERN_INFO, "%s: tx_run:%d\n", __FUNCTION__, i2s->tx_run);
	printk("%s: tx_run:%d\n", __FUNCTION__, i2s->tx_run);

	if (i2s->tx_run)
		return -EBUSY;

	i2s_tx_dma_start(i2s);
	i2s_start(i2s);
	i2s->tx_run = 1;
	
	
	/*
	int dma_ch = i2s->dma_tx_ch;
	struct scatter_dma_t *dma_desc;
	
	I2SCOMMON = 0x19;     // IISSCLK = Fosch(X1),       Set SCK/WS/CLKO of Tx and Rx as Common
    
    I2STCON  = 0x00;      // IIS Standard Format
    I2SRCON  = 0x00;      // IIS Standard Format
    
    I2SRMS   = 0x00;      // Slave
    I2STMS   = 0x01;      // Master
    
    I2STMCON  = 0x04;     // I2SMCLK = Fosch/4 = 11.2896MHz
                                  // I2SSCLK = 11.2896MHz/8 = 1411.2KHz
                                  // I2SWS = 1411.2KHz/32 = 44.1KHz
    //I2SRMCON = 0x04;
    //I2STCON  = 0x00;      // IIS Standard Format
    I2STFCLR = 0x01;      // Clear FIFO
    
    
    
    DMA_SRC_ADDR(dma_ch) = dma_desc[0]->srcaddr;
	DMA_DEST_ADDR(dma_ch) = dma_desc[0]->dstaddr;
	//DMA_LLI(dma_ch) = dma_desc->lli;
	DMA_CONTROL(dma_ch) = dma_desc[0]->control;
	DMA_CONFIG(dma_ch) = 0x00008a81;
	
	I2STSLVON = 0x0000
	*/
	
	

	return 0;
}

int tmpa910_i2s_tx_stop(struct tmpa910_i2s *i2s)
{
	if (!i2s->tx_run)
		return 0;

	/* Both rx and tx dma stopped */
	i2s_stop(i2s);
	i2s->curr_tx_desc = NULL;

	i2s->tx_run = 0;

	return 0;
}

int tmpa910_i2s_config_tx_dma(struct tmpa910_i2s *i2s, 
        unsigned char *cpu_buf, unsigned int phy_buf,
		int fragcount, size_t fragsize, size_t size)
{
    unsigned int count;
	dma_addr_t addr;
	u32 *map_cpu = (u32 *)cpu_buf;
	int i;

	i2s_printd(KERN_INFO, "%s( %p, %X, %d, %x, %x )\n", __FUNCTION__, cpu_buf, phy_buf,
			fragcount, fragsize, size);
	printk("%s( %p, %X, %d, %x, %x )\n", __FUNCTION__, cpu_buf, phy_buf,
			fragcount, fragsize, size);

	count = fragsize / size;

	/* for fragments larger than 16k words we use 2d dma, 
	 * denote fragecount as two numbers' mutliply and both of them 
	 * are less than 64k.*/
	if (count >= 0x1000)
	{
	    printk("Error: tx dma size too large %d\n", count);
		return -EINVAL;
	}

	if (i2s->dma_tx_desc)
	{
		dma_free_coherent(NULL, i2s->tx_desc_bytes, i2s->dma_tx_desc, 0);
	}

	i2s->dma_tx_desc = dma_alloc_coherent(NULL, fragcount * sizeof(struct scatter_dma_t), &addr, 0);
	i2s->tx_desc_bytes = fragcount * sizeof(struct scatter_dma_t);
	i2s->dma_tx_phydesc = addr;
	i2s->dma_tx_buf = phy_buf;

	if (!i2s->dma_tx_desc)
	{
		return -ENOMEM;
	}

	setup_tx_desc(i2s->dma_tx_desc, addr, phy_buf, fragcount, fragsize);
	
	//²âÊÔ³ÌÐò
	//memset(cpu_buf, 0x50, fragcount * fragsize);
	for(i=0; i<fragcount*fragsize/4; i++)
    {
        map_cpu[i] = (i << 2);
        //printk("\nmap_cpu[%x]= %d\n",i,map_cpu[i]);
    }

	printk("End i2s config tx dma ");
	return 0;
}

unsigned int tmpa910_i2s_curr_offset_tx(struct tmpa910_i2s *i2s)
{
	int dma_ch = i2s->dma_tx_ch;
	unsigned int addr, size;
	
    addr = DMA_SRC_ADDR(dma_ch);
    size = addr - i2s->dma_tx_buf;
    
    printk("size[%d]", size);

	return size;
}

static int i2s_check_status(struct tmpa910_i2s *i2s,
		unsigned int *i2s_stat,
		unsigned int *rx_stat,
		unsigned int *tx_stat)
{
	int status = 0;

	return status;
}

static void tx_handler(int dma_ch, void *dev_id)
{
	unsigned int tx_stat;
	struct tmpa910_i2s *i2s = dev_id;
	
	printk("DMA tx interrupt handler");

	printk("%s\n", __FUNCTION__);
	i2s_check_status(i2s, NULL, NULL, &tx_stat);

	if (i2s->tx_callback)
	{
		i2s->tx_callback(i2s->data);
	}
}

static void err_handler(int dma_ch, void *dev_id)
{
	unsigned int status;
	struct tmpa910_i2s *i2s = dev_id;

	printk("\nDMA error handler\n");
	i2s_printd(KERN_INFO, "%s\n", __FUNCTION__);
	printk("%s\n", __FUNCTION__);
	if (i2s_check_status(i2s, &status, NULL, NULL)) {
		printk(KERN_ERR "error checking status ??");
		printk("error checking status ??");
		return;
	}

	if (i2s->err_callback)
		i2s->err_callback(i2s->data);
}

struct tmpa910_i2s *tmpa910_i2s_init(
		int dma_rx, void (*rx_callback)(void*),
		int dma_tx, void (*tx_callback)(void*),
		int err_irq, void (*err_callback)(void*),
		void *data)
{
	struct tmpa910_i2s *i2s;

	i2s = kmalloc(sizeof(struct tmpa910_i2s), GFP_KERNEL);

	if (i2s == NULL)
		return NULL;

	memset(i2s, 0, sizeof(struct tmpa910_i2s));

 	i2s->dma_tx_ch = tmpa910_dma_request("I2S TX", 1, tx_handler, 
					err_handler, i2s);
	if (i2s->dma_tx_ch < 0)
	{
	    printk(KERN_ERR "unable to tx audio dma 0x%x\n", dma_tx);
	    printk("unable to tx audio dma 0x%x\n", dma_tx);
		goto __init_err2;
	}
	printk("dma_tx_ch = %d\n", i2s->dma_tx_ch);
	
	i2s->err_irq = err_irq;
	i2s->rx_callback = rx_callback;
	i2s->tx_callback = tx_callback;
	i2s->err_callback = err_callback;
	i2s->data = data;

	//printk("dma tx: %p\n", i2s->dma_tx_ch);

	return i2s;

__init_err2:
	kfree(i2s);
	return NULL;
}

void tmpa910_i2s_free(struct tmpa910_i2s *i2s)
{
	if (i2s == NULL)
		return;

	i2s_stop(i2s);
	if (i2s->dma_tx_desc)
	{
		dma_free_coherent(NULL, i2s->tx_desc_bytes, i2s->dma_tx_desc, 0);
	}

	tmpa910_dma_free(i2s->dma_tx_ch);

	kfree(i2s);
}
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/delay.h>

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

#include <asm/arch/dma.h>
#include <asm/arch/irqs.h>
#include <asm/arch/tmpa910-regs.h>

#include "tmpa910_i2s.h"
#include "wm8976.h"

#define I2S_DMA_RX   I2S0
#define I2S_DMA_TX   I2S1
#define I2S_IRQ_ERR  I2S_INT


#define CONFIG_SND_DEBUG	//Debug

#ifdef CONFIG_SND_DEBUG
#define snd_printk_marker() snd_printk(KERN_INFO "\n%s\n", __FUNCTION__)
#else
#define snd_printk_marker()
#endif

#undef CONFIG_SND_DEBUG_CURRPTR  /* causes output every frame! */
//#define CONFIG_SND_DEBUG_CURRPTR

#undef NOCONTROLS  /* define this to omit all the ALSA controls */

#define DRIVER_NAME	"WM8976-I2S"
#define CHIP_NAME	"Wolfson WM8976"
#define PCM_NAME	"WM8976_PCM"

/* Only one WM8976 soundcard is supported */
static struct platform_device *device = NULL;


/* Chip level */
#define WM8976_BUF_SZ 	0x10000  /* 64kb */
#define PCM_BUFFER_MAX	(WM8976_BUF_SZ / 2)

#define CHANNELS_OUTPUT	2
#define CHANNELS_INPUT	2
#define FRAGMENTS_MIN	2
#define FRAGMENTS_MAX	32

#define AUDIO_RATE_DEFAULT  44100

typedef struct snd_wm8976 wm8976_t;
typedef struct snd_pcm_substream snd_pcm_substream_t;
typedef struct snd_pcm_hardware snd_pcm_hardware_t;
typedef struct snd_pcm_hw_params snd_pcm_hw_params_t;
typedef struct snd_pcm_runtime snd_pcm_runtime_t;
typedef struct snd_pcm_ops snd_pcm_ops_t;

struct snd_wm8976 {

	struct snd_card    *card;
	struct tmpa910_i2s *i2s;
	spinlock_t    wm8976_lock;

	struct snd_pcm *pcm;

	int poll_reg;  /* index of the wm8976 register last queried */

	/* if non-null, current subtream running */
	snd_pcm_substream_t *rx_substream;
	/* if non-null, current subtream running */
	snd_pcm_substream_t *tx_substream;
};

/*======================================================*/
/*     WM8976 I2C INTERFACE DEFINE                      */
/*======================================================*/
#define I2C0SR_BUS_CHECK	0x00000020
#define I2C0SR_BUS_FREE		0x00000000

#define I2C0SR_ACK_CHECK	0x00000010
#define I2C0SR_ACK_ENABLE	0x00000000

static void i2c_bus_free_chk(void)
{
	u32 reg_data;
	do
	{
		reg_data = I2C0SR;
	}while( (reg_data & I2C0SR_BUS_CHECK) != I2C0SR_BUS_FREE );
}

static void i2c_ack_wait(void)
{
	u32 reg_data;
	printk("\ni2c: wait .... ");
	do
	{
		reg_data = I2C0SR;
	}while( (reg_data & I2C0SR_ACK_CHECK) != I2C0SR_ACK_ENABLE );
}

static void i2c_packet_send(u8 sub_addr, u8 data)
{
	printk("\ni2c: begin send packet");
	I2C0DBR = 0x34;
	I2C0CR2 = 0xf8;

	i2c_ack_wait();

	I2C0DBR = sub_addr;
	i2c_ack_wait();

	I2C0DBR = data;
	i2c_ack_wait();

	I2C0CR2 = 0xd8;
	i2c_bus_free_chk();
	printk("\ni2c: stop send packet");
}

static void init_wm8976_i2c(void)
{
    unsigned long flags;

	local_irq_save(flags);
	
	printk("\nInit I2S\n");
    /* I2S Register Set */
    GPIOLFR1  = 0x1f;     // Config IIS Function Port: I2SSCLK,I2S0MCLK,I2S0DATI,I2S0CLK,I2S0WS
    GPIOMFR1  = 0x04;     // Config IIS Function Port: I2S1DATO
    I2SCOMMON = 0x19;     // IISSCLK = Fosch(X1),       Set SCK/WS/CLKO of Tx and Rx as Common
    I2STMCON  = 0x04;     // I2SMCLK = Fosch/4 = 11.2896MHz
                                  // I2SSCLK = 11.2896MHz/8 = 1411.2KHz
                                  // I2SWS = 1411.2KHz/32 = 44.1KHz
    I2SRMCON = 0x04;
    I2STCON  = 0x00;      // IIS Standard Format
    I2STFCLR = 0x01;      // Clear FIFO
    I2SRMS   = 0x00;      // Slave
    I2STMS   = 0x00;      // Slave

	printk("\nInit I2C0\n");
    /* GPIO Setting for I2C0 Pin */
    GPIOCODE = 0xC0;       // Open-Drain Enable
	GPIOCFR1 = 0xC0;       // 1
	GPIOCFR2 = 0x00;       // 0
	GPIOCIE  = 0x00;       // 0 I2C Interrupt Disable

	/* Power On  WM8976: PR2 = 0*/
	GPIORDIR = 0x07;
	GPIORFR1 = 0x00;
	GPIORFR2 = 0x00;
    GPIORDATA = 0x00;
	
	/* GPIO Setting for I2C1 Pin */
	GPIOFODE = 0xC0;
	GPIOFFR1 = 0xC0;
	GPIOFFR2 = 0x00;
	GPIOFDIR = 0x00;
	GPIOFIE = 0x00;
    local_irq_restore(flags);

	i2c_bus_free_chk();
	
	I2C0CR2 = 0xc8;
	I2C0PRS = 0x19;
	I2C0CR1 = 0x13;

	printk("\nInit WM8960 codec\n");
	i2c_packet_send(0x00, 0x00);	//R0 = 0x00
	i2c_packet_send(0x02, 0x2d);	//R1 = 0x02d
	i2c_packet_send(0x04, 0x3f);	//R2 = 0x03f
	i2c_packet_send(0x07, 0xef);	//R3 = 0x1ef

	i2c_packet_send(0x05, 0x90);	//R2 = 0x190
	
	i2c_packet_send(0x14, 0x80);	//R10 = 0x080
	i2c_packet_send(0x17, 0xff);	//R11 = 0x1ff
	i2c_packet_send(0x19, 0xff);	//R12 = 0x1ff
	i2c_packet_send(0x30, 0x32);	//R24 = 0x032

	//sample rate is 44.1KHz
	i2c_packet_send(0x0d, 0x49);	//R6 = 0x149
	i2c_packet_send(0x48, 0x07);	//R36 = 0x007
	i2c_packet_send(0x4a, 0x21);	//R37 = 0x021
	i2c_packet_send(0x4d, 0x61);	//R38 = 0x161
	i2c_packet_send(0x4e, 0x27);	//R39 = 0x027

}

static void release_wm8976_i2c(void)
{
}

/*======================================*/
/* AUDIO CLOCK INTERFACE                */
static void enable_audio_sysclk(void)
{
}

static void disable_audio_sysclk(void)
{
}


/*************************************************************
 *                pcm methods
 *************************************************************/

static snd_pcm_hardware_t snd_wm8976_playback_hw = {
	.info = ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER ),
	.formats =      SNDRV_PCM_FMTBIT_S16_LE,
	.rates =	    SNDRV_PCM_RATE_44100,
	.rate_min =	    44100,
	.rate_max =	    44100,
	.channels_min =	    2,
	.channels_max =     2,
	.buffer_bytes_max = PCM_BUFFER_MAX,
	.period_bytes_min = 0x1000,     //4KB
	.period_bytes_max = 0x3000,     //8KB
	.periods_min =      FRAGMENTS_MIN,
	.periods_max =      FRAGMENTS_MAX,
};

static snd_pcm_hardware_t snd_wm8976_capture_hw = {
	.info = ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER ),
	.formats =          SNDRV_PCM_FMTBIT_S16_LE,
	.rates =            SNDRV_PCM_RATE_44100,
	.rate_min =         44100,
	.rate_max =         44100,
	.channels_min =     2,
	.channels_max =     2,
	.buffer_bytes_max = PCM_BUFFER_MAX,
	.period_bytes_min = 0x1000,     //4KB
	.period_bytes_max = 0x3000,     //8KB
	.periods_min =      FRAGMENTS_MIN,
	.periods_max =      FRAGMENTS_MAX,
};

static int snd_wm8976_playback_open(snd_pcm_substream_t *substream)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	printk("\nsnd playback open\n");
	//snd_printk_marker();
	chip->tx_substream = substream;
	//printk("\n1232321321\n");
	substream->runtime->hw = snd_wm8976_playback_hw;
	//printk("\n1232321321\n");

	return 0;
}

static int snd_wm8976_capture_open(snd_pcm_substream_t *substream)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	printk("\nsnd capture open\n");
	//snd_printk_marker();
	substream->runtime->hw = snd_wm8976_capture_hw;
	chip->rx_substream = substream;

	return 0;
}

static int snd_wm8976_playback_close(snd_pcm_substream_t *substream)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	//snd_printk_marker();
	printk("\nsnd playback close\n");
	chip->tx_substream = NULL;

	return 0;
}

static int snd_wm8976_capture_close(snd_pcm_substream_t *substream)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	//snd_printk_marker();
	printk("\nsnd capture close\n");
	chip->rx_substream = NULL;

	return 0;
}

//I2S in following
static int snd_wm8976_hw_params(snd_pcm_substream_t *substream,
					snd_pcm_hw_params_t *hwparams)
{
	printk("\nsnd hw params\n");
	//snd_printk_marker();

	/*
	*  Allocate all available memory for our DMA buffer.
	*  Necessary because we get a 4x increase in bytes for the 2 channel mode.
	*  (we lie to the ALSA midlayer through the hwparams data)
	*  We're relying on the driver not supporting full duplex mode
	*  to allow us to grab all the memory.
	*/
	//printk("params_buffer_bytes return %d\n", params_buffer_bytes(hwparams));
	if( snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hwparams)) < 0 )
		return -ENOMEM;
	return 0;
}

static int snd_wm8976_hw_free(snd_pcm_substream_t * substream)
{
	printk("\nsnd hw free\n");
	//snd_printk_marker();
	snd_pcm_lib_free_pages(substream);
	return 0;
}

static int snd_wm8976_playback_prepare(snd_pcm_substream_t *substream)
{

	wm8976_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;

	printk("\nsnd playback prepare\n");
	int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);

	int err = 0;
	int word_len = 4;

	//snd_assert((substream == chip->tx_substream), return -EINVAL);

	printk("\n%s channels:%d, period_bytes:0x%lx, periods:%d\n",
			__FUNCTION__, runtime->channels,
			frames_to_bytes(runtime, runtime->period_size),
			runtime->periods);
	//snd_printd(KERN_INFO "%s channels:%d, period_bytes:0x%lx, periods:%d\n",
	//		__FUNCTION__, runtime->channels,
	//		frames_to_bytes(runtime, runtime->period_size),
	//		runtime->periods);

	err = tmpa910_i2s_config_tx_dma(chip->i2s, runtime->dma_area, runtime->dma_addr,
			runtime->periods, fragsize_bytes, word_len);
	printk("\n%x %x %x %x %x\n",runtime->dma_area, runtime->dma_addr,
			runtime->periods, fragsize_bytes, word_len);
			

	return err;
}

static int snd_wm8976_capture_prepare(snd_pcm_substream_t *substream)
{
    printk("  snd_wm8976_capture_prepare\n");
	return 0;
}

static int snd_wm8976_playback_trigger(snd_pcm_substream_t *substream, int cmd)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	snd_printk_marker();

	spin_lock(&chip->wm8976_lock);
	switch (cmd)
	{
	case SNDRV_PCM_TRIGGER_START:
	    printk("  SNDRV_PCM_TRIGGER_START\n");
		tmpa910_i2s_tx_start(chip->i2s);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	    tmpa910_i2s_tx_stop(chip->i2s);
	    printk("  SNDRV_PCM_TRIGGER_STOP\n");
		break;
	default:
		spin_unlock(&chip->wm8976_lock);
		return -EINVAL;
	}
	spin_unlock(&chip->wm8976_lock);

	//snd_printd(KERN_INFO"playback cmd:%s\n", cmd?"start":"stop");
	printk("\nplayback cmd:%s\n", cmd?"start":"stop");

	return 0;
}

static int snd_wm8976_capture_trigger(snd_pcm_substream_t *substream, int cmd)
{
	wm8976_t *chip = snd_pcm_substream_chip(substream);

	snd_printk_marker();

	spin_lock(&chip->wm8976_lock);
	snd_assert(substream == chip->rx_substream, return -EINVAL);
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		printk("  SNDRV_PCM_TRIGGER_START\n");
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	    printk("  SNDRV_PCM_TRIGGER_STOP\n");
		break;
	default:
		spin_unlock(&chip->wm8976_lock);
		return -EINVAL;
	}
	spin_unlock(&chip->wm8976_lock);

	//snd_printd(KERN_ERR"capture cmd:%s\n", cmd ? "start" : "stop");
	printk("capture cmd:%s\n", cmd ? "start" : "stop");

	return 0;
}

static snd_pcm_uframes_t snd_wm8976_playback_pointer(snd_pcm_substream_t *substream)
{
	printk("\nsnd playback pointer\n");
	wm8976_t *chip = snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int dma_ch = chip->i2s->dma_tx_ch;
	unsigned int offset;

	//offset = tmpa910_i2s_curr_offset_tx(chip->i2s);
	
	//offset = bytes_to_frames(runtime, offset);
	offset = bytes_to_frames(runtime, DMA_SRC_ADDR(dma_ch)-runtime->dma_addr);
	if (offset >= runtime->buffer_size)
		offset = 0;
	
		
	printk("\n(%x)\n", offset);
	printk("\nruntime dma addr = %x\n", runtime->dma_addr);
	return offset;
}

static snd_pcm_uframes_t snd_wm8976_capture_pointer(snd_pcm_substream_t *substream)
{
    printk("  snd_wm8976_capture_pointer\n");
	return 0;
}

/* pcm method tables */
static snd_pcm_ops_t snd_wm8976_playback_ops = {
	.open      = snd_wm8976_playback_open,
	.close     = snd_wm8976_playback_close,
	.ioctl     = snd_pcm_lib_ioctl,
	.hw_params = snd_wm8976_hw_params,
	.hw_free   = snd_wm8976_hw_free,
	.prepare   = snd_wm8976_playback_prepare,
	.trigger   = snd_wm8976_playback_trigger,
	.pointer   = snd_wm8976_playback_pointer,
};

static snd_pcm_ops_t snd_wm8976_capture_ops = {
	.open  = snd_wm8976_capture_open,
	.close = snd_wm8976_capture_close,
	.ioctl = snd_pcm_lib_ioctl,
	.hw_params = snd_wm8976_hw_params,
	.hw_free   = snd_wm8976_hw_free,
	.prepare   = snd_wm8976_capture_prepare,
	.trigger   = snd_wm8976_capture_trigger,
	.pointer   = snd_wm8976_capture_pointer,
};

/*************************************************************
 *      card and device
 *************************************************************/
static int snd_wm8976_stop(struct snd_wm8976 *chip)
{
	printk("\nsnd card stop\n");
	snd_printk_marker();

	return 0;
}

static int snd_wm8976_dev_free(struct snd_device *device)
{
	struct snd_wm8976 *chip = (wm8976_t *)device->device_data;

	snd_printk_marker();

	return snd_wm8976_stop(chip);
}

static struct snd_device_ops snd_wm8976_ops = {
	.dev_free = snd_wm8976_dev_free,
};

static int snd_bf53x_wm8976_reset(wm8976_t *chip)
{
	return 0;
}

static int snd_wm8976_configure(wm8976_t *chip)
{
	int err = 0;
	struct tmpa910_i2s *i2s= chip->i2s;

	snd_printk_marker();

	snd_bf53x_wm8976_reset(chip);

	err = err || tmpa910_i2s_config_tx(i2s);
	
	if (err)
	{
		snd_printk(KERN_ERR "Unable to set i2s configuration\n");
		printk("Unable to set i2s configuration\n");
	}

	return err;
}

static void snd_wm8976_dma_rx(void *data)
{
	struct snd_wm8976 *wm8976 = data;
	
	snd_printk_marker();

	if (wm8976->rx_substream) {
		snd_pcm_period_elapsed(wm8976->rx_substream);
	}
}

static void snd_wm8976_dma_tx(void *data)
{
	struct snd_wm8976 *wm8976 = data;
	
	//snd_printk_marker();
	
	printk("\nsnd_pcm_period_elapsed\n");
	if (wm8976->tx_substream) {
		snd_pcm_period_elapsed(wm8976->tx_substream);
	}
}

static void snd_wm8976_i2s_err(void *data)
{
	printk(KERN_ERR DRIVER_NAME ":%s: err happened on i2s\n", __FUNCTION__);
	printk(":%s: err happened on i2s\n", __FUNCTION__);
}

static int __devinit snd_wm8976_pcm(struct snd_wm8976 *wm8976)
{
	struct snd_pcm *pcm;
	int err = 0;

	printk("\nsnd pcm new\n");
	//snd_printk_marker();

	/* 1 playback and 1 capture substream, of 2-8 channels each */
	if((err = snd_pcm_new(wm8976->card, PCM_NAME, 0, 1, 1, &pcm)) < 0)
	{
		return err;
	}

	/*
	 * this sets up our initial buffers and sets the dma_type to isa.
	 * isa works but I'm not sure why (or if) it's the right choice
	 * this may be too large, trying it for now
	 */
	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
			snd_dma_isa_data(), WM8976_BUF_SZ, WM8976_BUF_SZ);


	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wm8976_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wm8976_capture_ops);
	wm8976->pcm = pcm;
	pcm->info_flags = 0;

	strcpy(pcm->name, PCM_NAME);
	pcm->private_data = wm8976;

	return 0;
}

static int __devinit snd_wm8976_probe(struct platform_device *pdev)
{
	int err = 0;
	struct snd_card *card;
	struct snd_wm8976 *wm8976;
	struct tmpa910_i2s *i2s;
	char * id = "ID string for TMPA910 + WM8976 soundcard.";
	
	snd_printk_marker();
	
	if (device != NULL)
		return -ENOENT;

	card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_wm8976));
	if (card == NULL) {
		snd_printdd(KERN_DEBUG "%s: snd_card_new() failed\n", __FUNCTION__);
		printk("%s: snd_card_new() failed\n", __FUNCTION__);
		return -ENOMEM;
	}

	wm8976 = card->private_data;
	wm8976->card = card;
	//wm8976->samplerate = AUDIO_RATE_DEFAULT;
	if ((i2s = tmpa910_i2s_init(I2S_DMA_RX, snd_wm8976_dma_rx, I2S_DMA_TX, snd_wm8976_dma_tx,
			    I2S_IRQ_ERR, snd_wm8976_i2s_err, wm8976)) == NULL)
	{
		printk(KERN_ERR DRIVER_NAME ": Failed to find device on i2s\n");
		printk(": Failed to find device on i2s\n");
		err = -ENODEV;
		goto __i2s_err;
	}

	wm8976->i2s = i2s;

	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, wm8976, &snd_wm8976_ops);
	if (err)
	{
		goto __nodev;
	}

	if ((err = snd_wm8976_pcm(wm8976)) < 0)
	{
		goto __nodev;
	}

	if ((err = snd_wm8976_configure(wm8976)) < 0)
	{
	    printk("snd_wm8976_configure faild.\n");
		goto __nodev;
	}
	
	strcpy(card->driver, DRIVER_NAME);
	strcpy(card->shortname, CHIP_NAME);
	sprintf(card->longname, "%s at I2S rx/tx dma %d/%d err irq %d",
		  card->shortname,
		  I2S_DMA_RX, I2S_DMA_TX, I2S_IRQ_ERR);
	printk(card->longname, "%s at I2S rx/tx dma %d/%d err irq %d",
		  card->shortname,
		  I2S_DMA_RX, I2S_DMA_TX, I2S_IRQ_ERR);
		  
	snd_card_set_dev(card, &pdev->dev);

	if ((err = snd_card_register(card)) < 0)
	{
	    printk("snd_card_register faild.\n");
		goto __nodev;
	}

    printk("platform_set_drvdata card=%p\n", card);
	platform_set_drvdata(pdev, card);

	return 0;

__nodev:
	tmpa910_i2s_free(i2s);
__i2s_err:
	snd_card_free(card);
	return err;
}

static int __devexit snd_wm8976_remove(struct platform_device *pdev)
{
	struct snd_card *card;
	struct snd_wm8976 *wm8976;
	
	printk("  -> snd_wm8976_remove\n");

	card = platform_get_drvdata(pdev);
	wm8976 = card->private_data;

	snd_wm8976_stop(wm8976);
	tmpa910_i2s_free(wm8976->i2s);

	snd_card_free(card);
	platform_set_drvdata(pdev, NULL);

	return 0;
}

#define TMPA910_WM8976_DRIVER	"tmpa910_wm8976"
static struct platform_driver snd_wm8976_driver = {
	.probe		= snd_wm8976_probe,
	.remove		= __devexit_p(snd_wm8976_remove),
	.driver		= {
		.name	= DRIVER_NAME,
	},
};

static int __init snd_wm8976_init(void)
{
	int err;

	init_wm8976_i2c();
	enable_audio_sysclk();

	if ((err = platform_driver_register(&snd_wm8976_driver)) < 0)
		return err;
#if 0
	device = platform_device_register_simple(TMPA910_WM8976_DRIVER, -1, NULL, 0);
	if (!IS_ERR(device))
	{
		if (platform_get_drvdata(device))
		{
		    printk("\nsnd_wm8976_init ok\n");
			return 0;
		}
		printk("\nsnd_wm8976_init error\n");
		platform_device_unregister(device);
		err = -ENODEV;
	}
	else
	{
		err = PTR_ERR(device);
	}

	platform_driver_unregister(&snd_wm8976_driver);
#endif

	printk("\nsnd_wm8976_init err = %d\n", err);
	return err;
}

static void __exit snd_wm8976_exit(void)
{
	platform_device_unregister(device);
	platform_driver_unregister(&snd_wm8976_driver);
	release_wm8976_i2c();
	disable_audio_sysclk();
}

MODULE_AUTHOR("TOSHIBA <tmpa910@xxxxxxxxxxx>");
MODULE_DESCRIPTION("TMPA910/WM8976");
MODULE_LICENSE("GPL");

module_init(snd_wm8976_init);
module_exit(snd_wm8976_exit);
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux