static void pcm_in_register_show(void) { pcm_debug("PCMIN registers show:\n"); pcm_debug("\tAUDIN_FIFO1_START(0x%04x): 0x%08x\n", AUDIN_FIFO1_START, READ_MPEG_REG(AUDIN_FIFO1_START)); pcm_debug("\tAUDIN_FIFO1_END(0x%04x): 0x%08x\n", AUDIN_FIFO1_END, READ_MPEG_REG(AUDIN_FIFO1_END)); pcm_debug("\tAUDIN_FIFO1_PTR(0x%04x): 0x%08x\n", AUDIN_FIFO1_PTR, READ_MPEG_REG(AUDIN_FIFO1_PTR)); pcm_debug("\tAUDIN_FIFO1_RDPTR(0x%04x): 0x%08x\n", AUDIN_FIFO1_RDPTR, READ_MPEG_REG(AUDIN_FIFO1_RDPTR)); pcm_debug("\tAUDIN_FIFO1_CTRL(0x%04x): 0x%08x\n", AUDIN_FIFO1_CTRL, READ_MPEG_REG(AUDIN_FIFO1_CTRL)); pcm_debug("\tAUDIN_FIFO1_CTRL1(0x%04x): 0x%08x\n", AUDIN_FIFO1_CTRL1, READ_MPEG_REG(AUDIN_FIFO1_CTRL1)); pcm_debug("\tPCMIN_CTRL0(0x%04x): 0x%08x\n", PCMIN_CTRL0, READ_MPEG_REG(PCMIN_CTRL0)); pcm_debug("\tPCMIN_CTRL1(0x%04x): 0x%08x\n", PCMIN_CTRL1, READ_MPEG_REG(PCMIN_CTRL1)); }
unsigned int pcm_out_rd_ptr(void) { unsigned int value = READ_MPEG_REG(AUDOUT_FIFO_RPTR); pcm_debug("PCMOUT read pointer: 0x%08x\n", value); return value; }
static int aml_pcm2bt_new(struct snd_soc_pcm_runtime *rtd) { /* pcm_debug("enter %s\n", __FUNCTION__); */ int ret = 0; struct snd_soc_card *card = rtd->card; struct snd_pcm *pcm = rtd->pcm; struct snd_soc_dai *dai; dai = rtd->cpu_dai; pcm_debug("enter %s dai->name: %s dai->id: %d\n", __func__, dai->name, dai->id); if (!card->dev->dma_mask) card->dev->dma_mask = &aml_pcm2bt_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = DMA_BIT_MASK(32); if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = aml_pcm2bt_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = aml_pcm2bt_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) goto out; } out: return ret; }
static int aml_pcm2bt_copy_capture(struct snd_pcm_runtime *runtime, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count) { struct aml_pcm_runtime_data *prtd = runtime->private_data; signed short *hwbuf = (signed short *)(runtime->dma_area + frames_to_bytes(runtime, pos)); unsigned int rdptr = 0; int ret = 0; pcm_debug("enter %s channel: %d pos: %ld count: %ld\n", __func__, channel, pos, count); if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, count))) { pr_err("%s copy to user failed!\n", __func__); return -EFAULT; } else { /* memset(hwbuf, 0xff, frames_to_bytes(runtime, count)); */ rdptr = prtd->buffer_start + frames_to_bytes(runtime, pos) + frames_to_bytes(runtime, count); if (rdptr >= (prtd->buffer_start + prtd->buffer_size)) rdptr = prtd->buffer_start + prtd->buffer_size; pcm_in_set_rd_ptr(rdptr); } return ret; }
unsigned int pcm_out_wr_ptr(void) { unsigned int value = 0; value = READ_MPEG_REG(AUDOUT_BUF0_WPTR); pcm_debug("PCMOUT write pointer: 0x%08x\n", value); return value; }
static int aml_pcm2bt_copy_playback(struct snd_pcm_runtime *runtime, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count) { struct aml_pcm_runtime_data *prtd = runtime->private_data; unsigned char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos); unsigned int wrptr = 0; int ret = 0; pcm_debug("enter %s channel: %d pos: %ld count: %ld\n", __func__, channel, pos, count); if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, count))) { pr_err("%s copy from user failed!\n", __func__); return -EFAULT; } else { wrptr = prtd->buffer_start + frames_to_bytes(runtime, pos) + frames_to_bytes(runtime, count); if (wrptr >= (prtd->buffer_start + prtd->buffer_size)) wrptr = prtd->buffer_start + prtd->buffer_size; pcm_out_set_wr_ptr(wrptr); } return ret; }
static int aml_pcm2bt_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct aml_pcm_runtime_data *prtd = runtime->private_data; size_t size = params_buffer_bytes(params); int ret = 0; ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) pr_err("%s malloc_pages return: %d\n", __func__, ret); else { prtd->buffer_start = runtime->dma_addr; prtd->buffer_size = runtime->dma_bytes; pcm_debug("%s dma_addr: 0x%08x dma_bytes: 0x%x\n", __func__, runtime->dma_addr, runtime->dma_bytes); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { aml_pcm2bt_playback_phy_buffer_addr = runtime->dma_addr; aml_pcm2bt_playback_phy_buffer_size = runtime->dma_bytes; } else { aml_pcm2bt_capture_phy_buffer_addr = runtime->dma_addr; aml_pcm2bt_capture_phy_buffer_size = runtime->dma_bytes; } } return ret; }
unsigned int pcm_in_rd_ptr(void) { unsigned int value = READ_MPEG_REG(AUDIN_FIFO1_RDPTR); pcm_debug("PCMIN AUDIN_FIFO1_RDPTR: 0x%08x\n", value); return value; }
void pcm_in_set_buf(unsigned int addr, unsigned int size) { pcmin_buffer_addr = addr; pcmin_buffer_size = size; pcm_debug("PCMIN buffer start: 0x%08x size: 0x%08x\n", pcmin_buffer_addr, pcmin_buffer_size); }
void pcm_out_set_buf(unsigned int addr, unsigned int size) { pcmout_buffer_addr = addr; pcmout_buffer_size = size; pcm_debug("PCMOUT buffer addr: 0x%08x end: 0x%08x\n", pcmout_buffer_addr, pcmout_buffer_size); }
unsigned int pcm_out_set_wr_ptr(unsigned int value) { unsigned int old = READ_MPEG_REG(AUDOUT_BUF0_WPTR); WRITE_MPEG_REG(AUDOUT_BUF0_WPTR, value); pcm_debug("PCMOUT write pointer: 0x%08x -> 0x%08x\n", old, value); return old; }
unsigned int pcm_in_set_rd_ptr(unsigned int value) { unsigned int old = READ_MPEG_REG(AUDIN_FIFO1_RDPTR); WRITE_MPEG_REG(AUDIN_FIFO1_RDPTR, value); pcm_debug("PCMIN AUDIN_FIFO1_RDPTR: 0x%08x -> 0x%08x\n", old, value); return old; }
unsigned int pcm_in_fifo_int(void) { unsigned int value = 0; value = READ_MPEG_REG(AUDIN_FIFO_INT); pcm_debug("PCMIN AUDIN_FIFO_INT: 0x%08x\n", value); return value; }
static int aml_pcm2bt_timer_stop(struct aml_pcm_runtime_data *prtd) { pcm_debug("%s\n", __func__); spin_lock(&prtd->lock); prtd->running = 0; del_timer(&prtd->timer); spin_unlock(&prtd->lock); return 0; }
static int aml_pcm2bt_timer_start(struct aml_pcm_runtime_data *prtd) { pcm_debug("%s\n", __func__); spin_lock(&prtd->lock); aml_pcm2bt_timer_rearm(prtd); prtd->running = 1; spin_unlock(&prtd->lock); return 0; }
static snd_pcm_uframes_t aml_pcm2bt_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct aml_pcm_runtime_data *prtd = runtime->private_data; snd_pcm_uframes_t frames; pcm_debug("enter %s\n", __func__); frames = bytes_to_frames(runtime, (ssize_t) prtd->buffer_offset); return frames; }
static int aml_pcm2bt_timer_create(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct aml_pcm_runtime_data *prtd = runtime->private_data; pcm_debug("%s\n", __func__); init_timer(&prtd->timer); prtd->timer_period = 1; prtd->timer.data = (unsigned long)substream; prtd->timer.function = aml_pcm2bt_timer_callback; prtd->running = 0; return 0; }
static int aml_pcm2bt_silence(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { struct snd_pcm_runtime *runtime = substream->runtime; unsigned char *ppos = NULL; ssize_t n; pcm_debug("enter %s\n", __func__); n = frames_to_bytes(runtime, count); ppos = runtime->dma_area + frames_to_bytes(runtime, pos); memset(ppos, 0, n); return 0; }
unsigned int pcm_in_wr_ptr(void) { unsigned int writing = READ_MPEG_REG(AUDIN_FIFO1_PTR); unsigned int written = 0; unsigned int value = 0; WRITE_MPEG_REG(AUDIN_FIFO1_PTR, 1); written = READ_MPEG_REG(AUDIN_FIFO1_PTR); pcm_debug("PCMIN AUDIN_FIFO1_PTR: 0x%08x (0x%08x)\n", written, writing); //value = written; value = written & (~0x07); return value; }
static int aml_pcm2bt_hw_free(struct snd_pcm_substream *substream) { pcm_debug("enter %s\n", __func__); snd_pcm_lib_free_pages(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { aml_pcm2bt_playback_phy_buffer_addr = 0; aml_pcm2bt_playback_phy_buffer_size = 0; } else { aml_pcm2bt_capture_phy_buffer_addr = 0; aml_pcm2bt_capture_phy_buffer_size = 0; } return 0; }
static unsigned int aml_pcm_offset_tx(struct aml_pcm_runtime_data *prtd) { unsigned int value = 0; signed int diff = 0; value = pcm_out_rd_ptr(); diff = value - prtd->buffer_start; if (diff < 0) diff = 0; else if (diff >= prtd->buffer_size) diff = prtd->buffer_size; pcm_debug(KERN_DEBUG "%s value: 0x%08x offset: 0x%08x\n", __func__, value, diff); return (unsigned int)diff; }
static int aml_pcm2bt_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct aml_pcm_runtime_data *prtd; int ret; pcm_debug("enter %s\n", __func__); snd_soc_set_runtime_hwparams(substream, &aml_pcm2bt_hardware); /* Ensure that peroid size is a multiple of 32bytes */ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes); if (ret < 0) { pr_err("set period bytes constraint error\n"); goto out; } /* Ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { pr_err("set periods constraint error\n"); goto out; } prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); if (prtd == NULL) { pr_err("out of memory\n"); ret = -ENOMEM; goto out; } runtime->private_data = prtd; aml_pcm2bt_timer_create(substream); prtd->substream = substream; spin_lock_init(&prtd->lock); return 0; out: return ret; }
static void aml_pcm2bt_free(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; int stream; pcm_debug("enter %s\n", __func__); for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; if (!substream) continue; buf = &substream->dma_buffer; if (!buf->area) continue; dma_free_coherent(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; } }
static int aml_pcm2bt_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct aml_pcm_runtime_data *prtd = runtime->private_data; int ret = 0; pcm_debug("enter %s\n", __func__); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: aml_pcm2bt_timer_start(prtd); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: aml_pcm2bt_timer_stop(prtd); break; default: ret = -EINVAL; } return ret; }
void pcm_in_enable(int flag) { /* reset fifo */ RESET_FIFO: WRITE_MPEG_REG_BITS(AUDIN_FIFO1_CTRL, 1, 1, 1); WRITE_MPEG_REG(AUDIN_FIFO1_PTR, 0); if (READ_MPEG_REG(AUDIN_FIFO1_PTR) != READ_MPEG_REG(AUDIN_FIFO1_START)) goto RESET_FIFO; WRITE_MPEG_REG_BITS(AUDIN_FIFO1_CTRL, 0, 1, 1); /* reset pcmin */ WRITE_MPEG_REG_BITS(PCMIN_CTRL0, 1, 30, 1); WRITE_MPEG_REG_BITS(PCMIN_CTRL0, 0, 30, 1); /* disable fifo */ WRITE_MPEG_REG_BITS(AUDIN_FIFO1_CTRL, 0, 0, 1); /* disable pcmin */ WRITE_MPEG_REG_BITS(PCMIN_CTRL0, 0, 31, 1); if (flag) { /* set buffer start ptr end */ WRITE_MPEG_REG(AUDIN_FIFO1_START, pcmin_buffer_addr); WRITE_MPEG_REG(AUDIN_FIFO1_PTR, pcmin_buffer_addr); WRITE_MPEG_REG(AUDIN_FIFO1_END, pcmin_buffer_addr + pcmin_buffer_size - 8); /* fifo control */ WRITE_MPEG_REG(AUDIN_FIFO1_CTRL, (1 << 15) | // urgent request (1 << 11) | // channel (6 << 8) | // endian //(0 << 8) | // endian (2 << 3) | // PCMIN input selection (1 << 2) | // load address (0 << 1) | // reset fifo (1 << 0) // fifo enable ); /* fifo control1 */ WRITE_MPEG_REG(AUDIN_FIFO1_CTRL1, (0 << 4) | // data destination DDR (1 << 2) | // 16bits (0 << 0) // data position ); /* pcmin control1 */ WRITE_MPEG_REG(PCMIN_CTRL1, (0 << 29) | // external chip (0 << 28) | // external chip (1 << 27) | // using negedge of PCM clock to latch the input data (15 << 21) | // slot bit msb 16 clocks per slot (15 << 16) | // data msb 16bits data (1 << 0) // slot valid ); /* pcmin control0 */ WRITE_MPEG_REG(PCMIN_CTRL0, (1 << 31) | // pcmin enable (1 << 29) | // sync on clock posedge (0 << 16) | // FS SKEW (0 << 4) | // waithing 1 system clock cycles then sample the PCMIN singals (0 << 3) | // use clock counter to do the sample (0 << 2) | // fs not inverted. H = left, L = right (1 << 1) | // msb first (1 << 0) // left justified ); } pcm_debug("PCMIN %s\n", flag ? "enable" : "disable"); pcm_in_register_show(); }
static void pcm_out_register_show(void) { pcm_debug("PCMOUT registers show:\n"); pcm_debug("\tAUDOUT_BUF0_STA(0x%04x): 0x%08x\n", AUDOUT_BUF0_STA, READ_MPEG_REG(AUDOUT_BUF0_STA)); pcm_debug("\tAUDOUT_BUF0_EDA(0x%04x): 0x%08x\n", AUDOUT_BUF0_EDA, READ_MPEG_REG(AUDOUT_BUF0_EDA)); pcm_debug("\tAUDOUT_BUF0_WPTR(0x%04x): 0x%08x\n", AUDOUT_BUF0_WPTR, READ_MPEG_REG(AUDOUT_BUF0_WPTR)); pcm_debug("\tAUDOUT_FIFO_RPTR(0x%04x): 0x%08x\n", AUDOUT_FIFO_RPTR, READ_MPEG_REG(AUDOUT_FIFO_RPTR)); pcm_debug("\tAUDOUT_CTRL(0x%04x): 0x%08x\n", AUDOUT_CTRL, READ_MPEG_REG(AUDOUT_CTRL)); pcm_debug("\tAUDOUT_CTRL1(0x%04x): 0x%08x\n", AUDOUT_CTRL1, READ_MPEG_REG(AUDOUT_CTRL1)); pcm_debug("\tPCMOUT_CTRL0(0x%04x): 0x%08x\n", PCMOUT_CTRL0, READ_MPEG_REG(PCMOUT_CTRL0)); pcm_debug("\tPCMOUT_CTRL1(0x%04x): 0x%08x\n", PCMOUT_CTRL1, READ_MPEG_REG(PCMOUT_CTRL1)); pcm_debug("\tPCMOUT_CTRL2(0x%04x): 0x%08x\n", PCMOUT_CTRL2, READ_MPEG_REG(PCMOUT_CTRL2)); pcm_debug("\tPCMOUT_CTRL3(0x%04x): 0x%08x\n", PCMOUT_CTRL3, READ_MPEG_REG(PCMOUT_CTRL3)); }
void pcm_out_enable(int flag) { /* reset fifo */ WRITE_MPEG_REG_BITS(AUDOUT_CTRL, 1, 30, 1); WRITE_MPEG_REG_BITS(AUDOUT_CTRL, 0, 30, 1); /* reset pcmout */ WRITE_MPEG_REG_BITS(PCMOUT_CTRL0, 1, 30, 1); WRITE_MPEG_REG_BITS(PCMOUT_CTRL0, 0, 30, 1); /* disable fifo */ WRITE_MPEG_REG_BITS(AUDOUT_CTRL, 0, 31, 1); /* disable pcmout */ WRITE_MPEG_REG_BITS(PCMOUT_CTRL0, 0, 31, 1); if (flag) { /* set buffer start ptr end */ WRITE_MPEG_REG(AUDOUT_BUF0_STA, pcmout_buffer_addr); WRITE_MPEG_REG(AUDOUT_BUF0_WPTR, pcmout_buffer_addr); WRITE_MPEG_REG(AUDOUT_BUF0_EDA, pcmout_buffer_addr + pcmout_buffer_size - 8); /* fifo control */ WRITE_MPEG_REG(AUDOUT_CTRL, (0 << 31) | // fifo enable (0 << 30) | // soft reset (1 << 29) | // load address (0 << 22) | // use cbus AUDOUT BUFFER0 write pointer as the AUDOUT FIFO write pointer (52 << 15) | // data request size (64 << 8) | // buffer level to keep (0 << 7) | // buffer level control (1 << 6) | // DMA mode (1 << 5) | // circular buffer (0 << 4) | // use register set 0 always (1 << 3) | // urgent request (6 << 0) //endian ); WRITE_MPEG_REG(AUDOUT_CTRL, (1 << 31) | // fifo enable (0 << 30) | // soft reset (0 << 29) | // load address (0 << 22) | // use cbus AUDOUT BUFFER0 write pointer as the AUDOUT FIFO write pointer (52 << 15) | // data request size (64 << 8) | // buffer level to keep (0 << 7) | // buffer level control (1 << 6) | // DMA mode (1 << 5) | // circular buffer (0 << 4) | // use register set 0 always (1 << 3) | // urgent request (6 << 0) //endian ); /* pcmout control3 */ WRITE_MPEG_REG(PCMOUT_CTRL3, 0); // mute constant /* pcmout control2 */ WRITE_MPEG_REG(PCMOUT_CTRL2,(0 << 29) | // underrun use mute constant (0 << 22) | // 1 channel per frame (15 << 16) | // 16 bits per slot (1 << 0) // enable 1 slot ); /* pcmout control1 */ WRITE_MPEG_REG(PCMOUT_CTRL1,(1 << 30) | // data byte numbe n - 1? (0 << 28) | // use posedge of PCM clock to output data (1 << 27) // use negedge of pcm clock to check the fs ); /* pcmout control0 */ WRITE_MPEG_REG(PCMOUT_CTRL0,(1 << 31) | // enable (0 << 29) | // slave (1 << 28) | // sync on clock rising edge (0 << 27) | // data sample mode (1 << 15) | // sync on 4 system clock later ? (1 << 14) | // msb first (1 << 13) | // left justified (0 << 12) | // data position (3 << 6) | // sync fs with the slot bit counter. (0 << 0) // sync fs with frame slot counter. ); } pcm_debug("PCMOUT %s\n", flag ? "enable" : "disable"); pcm_out_register_show(); }