This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3352: McASP read

Part Number: AM3352


Tool/software: Linux

Hi.

I trying to read MCASP polling RBUF.  But, everytime RBUF is read Zero.

I expect when MCASP receive data RDATA bit will be set. So I check it, but it was never set.

I do not know what is problem. Plz. check my source code.

And is there any example to read MCASP. 

Thank you

- block

- source code

mcasp_open() -> audio_init() -> audio_run();  -> check rx state or RBUF

/*                                                     
    BodyFriend kdj6724
    mcasp_audio.c
 */                                                    
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/mm.h>
#include <linux/kdev_t.h>
#include <asm/page.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_device.h>

#include <asm/io.h>
#include "mcasp_audio.h"

static int mcasp_major = 0;
module_param(mcasp_major, int, 0);

#define SYS_INT_EDMA                      20
#define SYS_INT_MCATXINT0                      (80)
#define SYS_INT_MCARXINT0                      (81)
#define SYS_INT_MCATXINT1                      (82)
#define SYS_INT_MCARXINT1                      (83)

struct mcasp_edma_cc {
  struct device     *dev;
  struct edma_soc_info    *info;
  void __iomem      *base;
  int       id;
  bool        legacy_mode;

  /* eDMA3 resource information */
  unsigned      num_channels;
  unsigned      num_qchannels;
  unsigned      num_region;
  unsigned      num_slots;
  unsigned      num_tc;
  bool        chmap_exist;
  //enum dma_event_q    default_queue;

  /*
   * The slot_inuse bit for each PaRAM slot is clear unless the slot is
   * in use by Linux or if it is allocated to be used by DSP.
   */
  unsigned long *slot_inuse;

  struct dma_device   dma_slave;
  struct dma_device   *dma_memcpy;
  struct edma_chan    *slave_chans;
  struct edma_tc      *tc_list;
  int       dummy_slot;
};

/******************************************************************************
**                      INTERNAL MACRO DEFINITIONS
******************************************************************************/
/*
** Values which are configurable
*/
/* Slot size to send/receive data */
#define SLOT_SIZE                             32

/* Word size to send/receive data. Word size <= Slot size */
#define WORD_SIZE                             24

/* Sampling Rate which will be used by both transmit and receive sections */
#define SAMPLING_RATE                         (44100u)

/* Number of channels, L & R */
#define NUM_I2S_CHANNELS                      (2u) 

/* Number of samples to be used per audio buffer */
#define NUM_SAMPLES_PER_AUDIO_BUF             (2000u)

/* Number of buffers used per tx/rx */
#define NUM_BUF                               (3u)

/* Number of linked parameter set used per tx/rx */
#define NUM_PAR                               (2u)

/* Specify where the parameter set starting is */
#define PAR_ID_START                          (70u)

/* Number of samples in loop buffer */
#define NUM_SAMPLES_LOOP_BUF                  (10u)

/* AIC3106 codec address */
#define I2C_SLAVE_CODEC_AIC31                 (0x1Bu) 

/* McASP Serializer for Receive */
#define MCASP_XSER_RX                         (1u)

/* McASP Serializer for Transmit */
#define MCASP_XSER_TX                         (0u)

/*
** Below Macros are calculated based on the above inputs
*/
#define NUM_TX_SERIALIZERS                    ((NUM_I2S_CHANNELS >> 1) \
                                               + (NUM_I2S_CHANNELS & 0x01))
#define NUM_RX_SERIALIZERS                    ((NUM_I2S_CHANNELS >> 1) \
                                               + (NUM_I2S_CHANNELS & 0x01))
#define I2S_SLOTS                             ((1 << NUM_I2S_CHANNELS) - 1)

#define BYTES_PER_SAMPLE                      ((WORD_SIZE >> 3) \
                                               * NUM_I2S_CHANNELS)

#define AUDIO_BUF_SIZE                        (NUM_SAMPLES_PER_AUDIO_BUF \
                                               * BYTES_PER_SAMPLE)

#define TX_DMA_INT_ENABLE                     (EDMA3CC_OPT_TCC_SET \
                                               (EDMA3_CHA_MCASP1_TX) | (1 \
                                               << EDMA3CC_OPT_TCINTEN_SHIFT))
#define RX_DMA_INT_ENABLE                     (EDMA3CC_OPT_TCC_SET \
                                               (EDMA3_CHA_MCASP1_RX) | (1 \
                                               << EDMA3CC_OPT_TCINTEN_SHIFT))

#define PAR_RX_START                          (PAR_ID_START)
#define PAR_TX_START                          (PAR_RX_START + NUM_PAR)

/*
** Definitions which are not configurable 
*/
#define SIZE_PARAMSET                         (32u)
#define OPT_FIFO_WIDTH                        (0x02 << 8u)

void __iomem *mcasp0_base;
void __iomem *mcasp1_base;
void __iomem *ctrl_base;
void __iomem *cmper_base;
void __iomem *edma3ch_base; // edma3 channel controller
void __iomem *edma3ch0_base; // edma3 channel1 controller
void __iomem *edma3ch1_base; // edma3 channel2 controller
void __iomem *edma3ch2_base; // edma3 channel3 controller

/******************************************************************************
**                      INTERNAL VARIABLE DEFINITIONS
******************************************************************************/
static unsigned char loopBuf[NUM_SAMPLES_LOOP_BUF * BYTES_PER_SAMPLE] = {0};


/*
** Transmit buffers. If any new buffer is to be added, define it here and 
** update the NUM_BUF.
*/
static unsigned char txBuf0[AUDIO_BUF_SIZE];
static unsigned char txBuf1[AUDIO_BUF_SIZE];
static unsigned char txBuf2[AUDIO_BUF_SIZE];

/*
** Receive buffers. If any new buffer is to be added, define it here and 
** update the NUM_BUF.
*/
static unsigned char rxBuf0[AUDIO_BUF_SIZE];
static unsigned char rxBuf1[AUDIO_BUF_SIZE];
static unsigned char rxBuf2[AUDIO_BUF_SIZE];

/*
** Next buffer to receive data. The data will be received in this buffer.
*/
static volatile unsigned int nxtBufToRcv = 0;

/*
** The RX buffer which filled latest.
*/
static volatile unsigned int lastFullRxBuf = 0;

/*
** The offset of the paRAM ID, from the starting of the paRAM set.
*/
static volatile unsigned short parOffRcvd = 0;

/*
** The offset of the paRAM ID sent, from starting of the paRAM set.
*/
static volatile unsigned short parOffSent = 0;

/*
** The transmit buffer which was sent last.
*/
static volatile unsigned int lastSentTxBuf = NUM_BUF - 1;

/*
** The offset of the paRAM ID to be sent next, from starting of the paRAM set. 
*/
static volatile unsigned short parOffTxToSend = 0;

/******************************************************************************
**                      INTERNAL CONSTATNT DEFINITIONS
******************************************************************************/
/* Array of receive buffer pointers */
static unsigned int const rxBufPtr[NUM_BUF] =
       {   
           (unsigned int) rxBuf0,
           (unsigned int) rxBuf1,
           (unsigned int) rxBuf2
       };  

/* Array of transmit buffer pointers */
static unsigned int const txBufPtr[NUM_BUF] =
       {   
           (unsigned int) txBuf0,
           (unsigned int) txBuf1,
           (unsigned int) txBuf2
       };

/*
** Default paRAM for Transmit section. This will be transmitting from 
** a loop buffer.
*/
static struct EDMA3CCPaRAMEntry const txDefaultPar = 
       {   
           (unsigned int)(OPT_FIFO_WIDTH), /* Opt field */
           (unsigned int)loopBuf, /* source address */
           (unsigned short)(BYTES_PER_SAMPLE), /* aCnt */
           (unsigned short)(NUM_SAMPLES_LOOP_BUF), /* bCnt */ 
           (unsigned int) SOC_MCASP_1_DATA_REGS, /* dest address */
           (short) (BYTES_PER_SAMPLE), /* source bIdx */
           (short)(0), /* dest bIdx */
           (unsigned short)(PAR_TX_START * SIZE_PARAMSET), /* link address */
           (unsigned short)(0), /* bCnt reload value */
           (short)(0), /* source cIdx */
           (short)(0), /* dest cIdx */
           (unsigned short)1 /* cCnt */
       };  

/*
** Default paRAM for Receive section.  
*/
static struct EDMA3CCPaRAMEntry const rxDefaultPar =
       {   
           (unsigned int)(OPT_FIFO_WIDTH), /* Opt field */
           (unsigned int)SOC_MCASP_1_DATA_REGS, /* source address */
           (unsigned short)(BYTES_PER_SAMPLE), /* aCnt */
           (unsigned short)(1), /* bCnt */
           (unsigned int)rxBuf0, /* dest address */
           (short) (0), /* source bIdx */
           (short)(BYTES_PER_SAMPLE), /* dest bIdx */
           (unsigned short)(PAR_RX_START * SIZE_PARAMSET), /* link address */
           (unsigned short)(0), /* bCnt reload value */
           (short)(0), /* source cIdx */
           (short)(0), /* dest cIdx */
           (unsigned short)1 /* cCnt */
       };

/******************************************************************************
**                          FUNCTION DEFINITIONS
******************************************************************************/
/*
** Assigns loop job for a parameter set
*/
static void ParamTxLoopJobSet(volatile void __iomem *dmaAddr, unsigned short parId)
{
    EDMA3CCPaRAMEntry paramSet;
    
    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET - 2); 
  
    /* link the paRAM to itself */
    paramSet.linkAddr = parId * SIZE_PARAMSET;

    EDMA3SetPaRAM(dmaAddr, parId, &paramSet);
}

/*
** Initializes the DMA parameters.
** The RX basic paRAM set(channel) is 0 and TX basic paRAM set (channel) is 1.
**
** The RX paRAM set 0 will be initialized to receive data in the rx buffer 0.
** The transfer completion interrupt will not be enabled for paRAM set 0;
** paRAM set 0 will be linked to linked paRAM set starting (PAR_RX_START) of RX.
** and further reception only happens via linked paRAM set. 
** For example, if the PAR_RX_START value is 40, and the number of paRAMS is 2, 
** reception paRAM set linking will be initialized as 0-->40-->41-->40
**
** The TX paRAM sets will be initialized to transmit from the loop buffer.
** The size of the loop buffer can be configured.   
** The transfer completion interrupt will not be enabled for paRAM set 1;
** paRAM set 1 will be linked to linked paRAM set starting (PAR_TX_START) of TX.
** All other paRAM sets will be linked to itself.
** and further transmission only happens via linked paRAM set.
** For example, if the PAR_RX_START value is 42, and the number of paRAMS is 2, 
** So transmission paRAM set linking will be initialized as 1-->42-->42, 43->43. 
*/

static void I2SDMAParamInit(volatile void __iomem *dmaAddr)
{
    EDMA3CCPaRAMEntry paramSet;
    int idx;
 
    /* Initialize the 0th paRAM set for receive */ 
    memcpy(&paramSet, &rxDefaultPar, SIZE_PARAMSET - 2);

    EDMA3SetPaRAM(dmaAddr, EDMA3_CHA_MCASP1_RX, &paramSet);

    /* further paramsets, enable interrupt */
    paramSet.opt |= RX_DMA_INT_ENABLE;
 
    for(idx = 0 ; idx < NUM_PAR; idx++)
    {                   
        paramSet.destAddr = rxBufPtr[idx];

        paramSet.linkAddr = (PAR_RX_START + ((idx + 1) % NUM_PAR))
                             * (SIZE_PARAMSET);        

        paramSet.bCnt =  NUM_SAMPLES_PER_AUDIO_BUF;
        
        /* 
        ** for the first linked paRAM set, start receiving the second
        ** sample only since the first sample is already received in
        ** rx buffer 0 itself.
        */   
        if( 0 == idx)
        {
            paramSet.destAddr += BYTES_PER_SAMPLE;
            paramSet.bCnt -= BYTES_PER_SAMPLE;
        }

        EDMA3SetPaRAM(dmaAddr, (PAR_RX_START + idx), &paramSet);
    }

    /* Initialize the required variables for reception */
    nxtBufToRcv = idx % NUM_BUF;
    lastFullRxBuf = NUM_BUF - 1;
    parOffRcvd = 0;

    /* Initialize the 1st paRAM set for transmit */
    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET - 2);

    EDMA3SetPaRAM(dmaAddr, EDMA3_CHA_MCASP1_TX, &paramSet);

    /* rest of the params, enable loop job */
    for(idx = 0 ; idx < NUM_PAR; idx++)
    {
        ParamTxLoopJobSet(dmaAddr, PAR_TX_START + idx);
    }

    /* Initialize the variables for transmit */
    parOffSent = 0;
    lastSentTxBuf = NUM_BUF - 1;
}

/*
** Configures the McASP Transmit Section in I2S mode.
*/
static void McASPI2STxConfigure(volatile void __iomem *baseAddr)
{
    McASPTxReset(baseAddr);

    /* Enable the FIFOs for DMA transfer */
    McASPWriteFifoEnable(baseAddr + 0x1000, 1, 1);

    /* Set I2S format in the transmitter/receiver format units */
    McASPTxFmtI2SSet(baseAddr, WORD_SIZE, SLOT_SIZE,
                     MCASP_TX_MODE_DMA);

    /* Configure the frame sync. I2S shall work in TDM format with 2 slots */
    McASPTxFrameSyncCfg(baseAddr, 2, MCASP_TX_FS_WIDTH_WORD,
                        MCASP_TX_FS_INT_BEGIN_ON_FALL_EDGE);

    /* configure the clock for transmitter */
    McASPTxClkCfg(baseAddr, MCASP_TX_CLK_INTERNAL, 0, 0);
    McASPTxClkPolaritySet(baseAddr, MCASP_TX_CLK_POL_FALL_EDGE);
    McASPTxClkCheckConfig(baseAddr, MCASP_TX_CLKCHCK_DIV32,
                          0x00, 0xFF);
    
    /* Enable synchronization of RX and TX sections  */
    McASPTxRxClkSyncEnable(baseAddr);

    /* Enable the transmitter/receiver slots. I2S uses 2 slots */
    McASPTxTimeSlotSet(baseAddr, I2S_SLOTS);

    /*
    ** Set the serializers, Currently only one serializer is set as
    ** transmitter and one serializer as receiver.
    */
    McASPSerializerTxSet(baseAddr, MCASP_XSER_TX);

    /*
    ** Configure the McASP pins 
    ** Input - Frame Sync, Clock and Serializer Rx
    ** Output - Serializer Tx is connected to the input of the codec 
    */
    McASPPinMcASPSet(baseAddr, 0xFFFFFFFF);
    McASPPinDirOutputSet(baseAddr, MCASP_PIN_AXR(MCASP_XSER_TX) 
                                  | MCASP_PIN_ACLKX | MCASP_PIN_AFSX);
}

static void McASPI2SRxConfigure(volatile void __iomem *baseAddr)
{
    McASPRxReset(baseAddr);

    /* Enable the FIFOs for DMA transfer */
    McASPReadFifoEnable(baseAddr + 0x1000, 1, 1);

    /* Set I2S format in the transmitter/receiver format units */
    McASPRxFmtI2SSet(baseAddr, WORD_SIZE, SLOT_SIZE,
                     MCASP_RX_MODE_DMA);

    /* Configure the frame sync. I2S shall work in TDM format with 2 slots */
    McASPRxFrameSyncCfg(baseAddr, 2, MCASP_RX_FS_WIDTH_WORD,
                        MCASP_RX_FS_INT_BEGIN_ON_FALL_EDGE);

    /* configure the clock for receiver */
    McASPRxClkCfg(baseAddr, MCASP_RX_CLK_EXTERNAL, 0, 0);
    McASPRxClkPolaritySet(baseAddr, MCASP_RX_CLK_POL_FALL_EDGE);
    McASPRxClkCheckConfig(baseAddr, MCASP_RX_CLKCHCK_DIV32,
                          0x00, 0xFF);

    /* Enable synchronization of RX and TX sections  */
    McASPTxRxClkSyncEnable(baseAddr);

    /* Enable the transmitter/receiver slots. I2S uses 2 slots */
    McASPRxTimeSlotSet(baseAddr, I2S_SLOTS);

    /*
    ** Set the serializers, Currently only one serializer is set as
    ** transmitter and one serializer as receiver.
    */
    McASPSerializerRxSet(baseAddr, MCASP_XSER_RX);

    /*
    ** Configure the McASP pins 
    ** Input - Frame Sync, Clock and Serializer Rx
    ** Output - Serializer Tx is connected to the input of the codec 
    */
    McASPPinMcASPSet(baseAddr, 0xFFFFFFFF);
    McASPPinDirInputSet(baseAddr, MCASP_PIN_AFSR
                                | MCASP_PIN_ACLKR
                                | MCASP_PIN_AXR(MCASP_XSER_RX));
}

/*  
** Activates the data transmission/reception
** The DMA parameters shall be ready before calling this function.
*/  
static void I2SDataTxActivate(volatile void __iomem *mcaspAddr,
                                volatile void __iomem *dmaAddr)
{
    /* Start the clocks */
    McASPTxClkStart(mcaspAddr, MCASP_TX_CLK_EXTERNAL);
    
    /* Enable EDMA for the transfer */
    EDMA3EnableTransfer(dmaAddr, EDMA3_CHA_MCASP1_TX,
                        EDMA3_TRIG_MODE_EVENT);
    
    /* Activate the  serializers */
    //McASPRxSerActivate(mcaspAddr);
    McASPTxSerActivate(mcaspAddr);

    /* make sure that the XDATA bit is cleared to zero */
    while(McASPTxStatusGet(mcaspAddr) & MCASP_TX_STAT_DATAREADY);

    /* Activate the state machines */ 
    McASPTxEnable(mcaspAddr);
}

/*  
** Activates the data transmission/reception
** The DMA parameters shall be ready before calling this function.
*/  
static void I2SDataRxActivate(volatile void __iomem *mcaspAddr,
                                volatile void __iomem *dmaAddr)
{
    /* Start the clocks */
    McASPRxClkStart(mcaspAddr, MCASP_RX_CLK_EXTERNAL);
    
    /* Enable EDMA for the transfer */
    EDMA3EnableTransfer(dmaAddr, EDMA3_CHA_MCASP1_RX,
                        EDMA3_TRIG_MODE_EVENT);
    
    /* Activate the  serializers */
    McASPRxSerActivate(mcaspAddr);

    /* Activate the state machines */ 
    McASPRxEnable(mcaspAddr);
}

/*
** Activates the DMA transfer for a parameterset from the given buffer.
*/
static void BufferTxDMAActivate(volatile void __iomem *dmaAddr, unsigned int txBuf, 
                                unsigned short numSamples, unsigned short parId, unsigned short linkPar)
{
    EDMA3CCPaRAMEntry paramSet;
    
    /* Copy the default paramset */
    memcpy(&paramSet, &txDefaultPar, SIZE_PARAMSET - 2);
    
    /* Enable completion interrupt */
    paramSet.opt |= TX_DMA_INT_ENABLE; 
    paramSet.srcAddr =  txBufPtr[txBuf];
    paramSet.linkAddr = linkPar * SIZE_PARAMSET;
    paramSet.bCnt = numSamples;

    EDMA3SetPaRAM(dmaAddr, parId, &paramSet);
}


/*
** Activates the DMA transfer for a parameter set from the given buffer.
*/
static void BufferRxDMAActivate(volatile void __iomem *dmaAddr, unsigned int rxBuf, unsigned short parId,
                                unsigned short parLink)
{
    EDMA3CCPaRAMEntry paramSet;

    /* Copy the default paramset */
    memcpy(&paramSet, &rxDefaultPar, SIZE_PARAMSET - 2);

    /* Enable completion interrupt */
    paramSet.opt |= RX_DMA_INT_ENABLE;
    paramSet.destAddr =  rxBufPtr[rxBuf];
    paramSet.bCnt =  NUM_SAMPLES_PER_AUDIO_BUF;
    paramSet.linkAddr = parLink * SIZE_PARAMSET ;

    EDMA3SetPaRAM(dmaAddr, parId, &paramSet);
}

/*
** This function will be called once receive DMA is completed
*/
static void McASPRxDMAComplHandler(void)
{
    unsigned short nxtParToUpdate;

    /*  
    ** Update lastFullRxBuf to indicate a new buffer reception
    ** is completed.
    */
    lastFullRxBuf = (lastFullRxBuf + 1) % NUM_BUF;
    nxtParToUpdate =  PAR_RX_START + parOffRcvd;  
    parOffRcvd = (parOffRcvd + 1) % NUM_PAR;
 
    /*  
    ** Update the DMA parameters for the received buffer to receive
    ** further data in proper buffer
    */
    BufferRxDMAActivate(edma3ch_base, nxtBufToRcv, nxtParToUpdate,
                        PAR_RX_START + parOffRcvd);
    
    /* update the next buffer to receive data */ 
    nxtBufToRcv = (nxtBufToRcv + 1) % NUM_BUF;
}

/*
** This function will be called once transmit DMA is completed
*/
static void McASPTxDMAComplHandler(void)
{
    ParamTxLoopJobSet(edma3ch_base, (unsigned short)(PAR_TX_START + parOffSent));

    parOffSent = (parOffSent + 1) % NUM_PAR;
}

static irqreturn_t mcasp_irq_handler(int irq, void *data) {
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  return IRQ_HANDLED;
}

  //struct edma_info *prtd;
static int mcasp_irq_enable(void) {
  int ret = -1;
  //struct edma_info *prtd;
  
  //prtd = kzalloc(sizeof(struct edma_info), GFP_KERNEL);
  
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  //ret = request_irq(SYS_INT_EDMA, mcasp_irq_handler, IRQF_SHARED, "bd-edma", prtd);
  printk("[kdj6724] %s(%d) ret:%d\n", __FUNCTION__, __LINE__, ret);
  return 0;
}

static int audio_init(void)
{
  ctrl_base = ioremap(SOC_CONTROL_REGS, SOC_CONTROL_SIZE);  
  McASP1PinMuxSetup(ctrl_base);

  mcasp0_base = ioremap(SOC_MCASP_0_CFG, MCASP0_ADDR_SIZE);  
  mcasp1_base = ioremap(SOC_MCASP_1_CFG, MCASP1_ADDR_SIZE);  
  cmper_base = ioremap(SOC_CM_PER, CMPER_ADDR_SIZE);  
  edma3ch_base = ioremap(SOC_EDMA30CC_0_REGS, SOC_EDMA30CC_0_SIZE);  
  edma3ch0_base = ioremap(SOC_EDMA3CH_0_REG, SOC_EDMA3CH_SIZE);  
  edma3ch1_base = ioremap(SOC_EDMA3CH_1_REG, SOC_EDMA3CH_SIZE);  
  edma3ch2_base = ioremap(SOC_EDMA3CH_2_REG, SOC_EDMA3CH_SIZE);  

  McASPModuleClkConfig(cmper_base, CM_PER_MCASP1_CLKCTRL);
  McASPModuleClkConfig(cmper_base, CM_PER_MCASP0_CLKCTRL);
  EDMAModuleClkConfig(cmper_base, edma3ch0_base, edma3ch1_base, edma3ch2_base);
  EDMA3Init(edma3ch_base, 0);

  /* Request EDMA channels */
  EDMA3RequestChannel(edma3ch_base, EDMA3_CHANNEL_TYPE_DMA,
                      EDMA3_CHA_MCASP1_TX, EDMA3_CHA_MCASP1_TX, 0); 
  EDMA3RequestChannel(edma3ch_base, EDMA3_CHANNEL_TYPE_DMA,
                      EDMA3_CHA_MCASP1_RX, EDMA3_CHA_MCASP1_RX, 0);

  /* Initialize the DMA parameters */
  I2SDMAParamInit(edma3ch_base);

  /* Configure the McASP for I2S */
  McASPI2SRxConfigure(mcasp0_base);
  McASPI2STxConfigure(mcasp1_base);

  I2SDataRxActivate(mcasp0_base, edma3ch_base);
  I2SDataTxActivate(mcasp1_base, edma3ch_base);

	return 0;
}

void hex_dump(unsigned int *buf, size_t len) {
  size_t index=0;
  if (buf == NULL || len <= 0) {
    printk("[kdj6724] hex_dump error\n");
    return;
  }

  printk("\n");
  for (index=0; index<len; index++) {
    if (index%16 == 0)
      printk("\n");
    else
      printk("0x%04x ", buf[index]);
  }
  printk("\n");
}

int audio_run(void) {
  unsigned short parToSend;
  unsigned short parToLink;
  unsigned int index = 0;

  printk("Rx \n");
  McASPRegDump(mcasp0_base);
  printk("Tx\n");
  printk("\n");
  McASPRegDump(mcasp1_base);
  printk("\n");
#if 0 // read
  while(1) {
      McASPRxDMAComplHandler();
      while(McASPRxStatusGet(mcasp0_base) & MCASP_RX_STAT_DATAREADY);
      hex_dump((void *)rxBufPtr[0], 32);
      hex_dump((void *)rxBufPtr[1], 32);
      hex_dump((void *)rxBufPtr[2], 32);
  }
#endif

#if 1 // read RDATA reg.
  printk("state: 0x%08x\n", McASPRxStatusGet(mcasp0_base));
#endif

  return 0;
}

/*
 * Open the device; in fact, there's nothing to do here.
 */
static int mcasp_open(struct inode *inode, struct file *filp) {
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  //mcasp_irq_enable();
  audio_init();
  audio_run();
  return 0;
}

/*
 * Closing is just as simpler.
 */
static int mcasp_release(struct inode *inode, struct file *filp) {
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  return 0;
}

static void mcasp_setup_cdev(struct cdev *dev, int minor,
    struct file_operations *fops)
{
  int err, devno = MKDEV(mcasp_major, minor);

  cdev_init(dev, fops);
  dev->owner = THIS_MODULE;
  dev->ops = fops;
  err = cdev_add (dev, devno, 1);
  /* Fail gracefully if need be */
  if (err)
    printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
}

static struct file_operations mcasp_ops = { 
  .owner   = THIS_MODULE,
  .open    = mcasp_open,
  .release = mcasp_release,
};

#define MAX_MCASP_DEV   2
static struct cdev mcaspDev[MAX_MCASP_DEV];

#define EDMA_BINDING_LEGACY 0
#define EDMA_BINDING_TPCC 1
static const struct of_device_id edma_of_ids[] = {
  {
    .compatible = "ti,edma3",
    .data = (void *)EDMA_BINDING_LEGACY,
  },
  {
    .compatible = "ti,edma3-tpcc",
    .data = (void *)EDMA_BINDING_TPCC,
  },
  {}
};

static int mcasp_audio_probe(struct platform_device *pdev) {
  struct edma_soc_info  *info = pdev->dev.platform_data;
  struct mcasp_edma_cc    *ecc;
  struct device_node  *node = pdev->dev.of_node;
  struct device   *dev = &pdev->dev;
  char      *irq_name;
  struct resource   *mem;
  bool      legacy_mode = true;
  int ret = -1;
  int irq;

  if (node) {
    const struct of_device_id *match;

    match = of_match_node(edma_of_ids, node);
    if (match && (u32)match->data == EDMA_BINDING_TPCC)
      legacy_mode = false;
  }

  if (!pdev->dev.platform_data && !pdev->dev.of_node) {
    dev_err(&pdev->dev, "No platform data supplied\n");
    return -EINVAL;
  }

  ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
  if (!ecc) {
    dev_err(dev, "Can't allocate controller\n");
    return -ENOMEM;
  }

  ecc->dev = dev;
  ecc->id = pdev->id;
  mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "edma3_cc");
  if (!mem) {
    dev_dbg(dev, "mem resource not found, using index 0\n");
    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!mem) {
      dev_err(dev, "no mem resource?\n");
      return -ENODEV;
    }
  }
  
  //irq = platform_get_irq_byname(pdev, "edma3_ccint");
  //irq_name = devm_kasprintf(dev, GFP_KERNEL, "bd-edma");
  //ret = devm_request_irq(dev, SYS_INT_EDMA, mcasp_irq_handler, 0, irq_name, ecc);

  return 0;
}

static int mcasp_audio_remove(struct platform_device *pdev) {
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  return 0;
}

static struct platform_driver mcasp_edma_driver = {
  .probe    = mcasp_audio_probe,
  .remove   = mcasp_audio_remove,
  .driver = {
    .name = "mcasp-audio",
    //.pm = &edma_pm_ops,
    .of_match_table = edma_of_ids,
  },
};

int __init init_mcasp_audio(void)
{
  int result = 0;
  printk("[kdj6724] %s(%d)\n", __FUNCTION__, __LINE__);
  dev_t dev = MKDEV(mcasp_major, 0);
  result = alloc_chrdev_region(&dev, 0, 2, "mcasp-audio");
  mcasp_major = MAJOR(dev);

  if (result < 0) {
    printk(KERN_WARNING "unable to get major %d\n", mcasp_major);
    return result;
  }
  if (mcasp_major == 0)
    mcasp_major = result;

  mcasp_setup_cdev(mcaspDev, 0, &mcasp_ops);
  platform_driver_register(&mcasp_edma_driver);

  return result;
}

void __exit exit_mcasp_audio(void)
{
  iounmap(mcasp0_base);
  iounmap(mcasp1_base);
  iounmap(ctrl_base);
  iounmap(cmper_base);
  iounmap(edma3ch_base);
  iounmap(edma3ch0_base);
  iounmap(edma3ch1_base);
  iounmap(edma3ch2_base);
  //free_irq(SYS_INT_EDMA, prtd);
  platform_driver_unregister(&mcasp_edma_driver);
}

module_init(init_mcasp_audio);
module_exit(exit_mcasp_audio);

MODULE_AUTHOR("kdj6724");
MODULE_DESCRIPTION("Bodyfriend MCASP Audio driver");
MODULE_LICENSE("GPL");