diff options
Diffstat (limited to 'sound/soc/fsl/imx-pcm-rpmsg.c')
-rw-r--r-- | sound/soc/fsl/imx-pcm-rpmsg.c | 423 |
1 files changed, 321 insertions, 102 deletions
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index fc48451083bc..03f184a458ec 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -25,18 +25,19 @@ struct i2s_info *i2s_info_g; -static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { +static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = IMX_SAI_DMABUF_SIZE, .period_bytes_min = 512, .period_bytes_max = 65532, /* Limited by SDMA engine */ .periods_min = 2, - .periods_max = 255, + .periods_max = 6000, .fifo_size = 0, }; @@ -49,37 +50,36 @@ static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; - struct i2s_rpmsg_s *rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; - - rpmsg_tx->param.rate = params_rate(params); - rpmsg_rx->param.rate = params_rate(params); - if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) { - rpmsg_tx->param.format = RPMSG_S16_LE; - rpmsg_rx->param.format = RPMSG_S16_LE; - } else { - rpmsg_tx->param.format = RPMSG_S24_LE; - rpmsg_rx->param.format = RPMSG_S24_LE; - } + struct i2s_rpmsg *rpmsg; - if (params_channels(params) == 1) { - rpmsg_tx->param.channels = RPMSG_CH_LEFT; - rpmsg_rx->param.channels = RPMSG_CH_LEFT; - } else { - rpmsg_tx->param.channels = RPMSG_CH_STEREO; - rpmsg_rx->param.channels = RPMSG_CH_STEREO; - } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_HW_PARAM]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_HW_PARAM]; + + rpmsg->send_msg.param.rate = params_rate(params); + + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + rpmsg->send_msg.param.format = RPMSG_S16_LE; + else if (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE) + rpmsg->send_msg.param.format = RPMSG_S24_LE; + else + rpmsg->send_msg.param.format = RPMSG_S32_LE; + + if (params_channels(params) == 1) + rpmsg->send_msg.param.channels = RPMSG_CH_LEFT; + else + rpmsg->send_msg.param.channels = RPMSG_CH_STEREO; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - rpmsg_tx->header.cmd = I2S_TX_HW_PARAM; - i2s_info->send_message(rpmsg_tx, i2s_info); - } else { - rpmsg_rx->header.cmd = I2S_RX_HW_PARAM; - i2s_info->send_message(rpmsg_rx, i2s_info); - } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_HW_PARAM; + else + rpmsg->send_msg.header.cmd = I2S_RX_HW_PARAM; + + i2s_info->send_message(rpmsg, i2s_info); return 0; } @@ -93,29 +93,62 @@ static int imx_rpmsg_pcm_hw_free(struct snd_pcm_substream *substream) static snd_pcm_uframes_t imx_rpmsg_pcm_pointer( struct snd_pcm_substream *substream) { - struct dma_tx_state state; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - unsigned int buf_size; unsigned int pos = 0; - unsigned long flags; + struct i2s_rpmsg *rpmsg; + int buffer_tail = 0; - spin_lock_irqsave(&i2s_info->lock[substream->stream], flags); - state.residue = (i2s_info->num_period[substream->stream] - - rpmsg->param.buffer_tail) - * rpmsg->param.period_size; - spin_unlock_irqrestore(&i2s_info->lock[substream->stream], flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; - buf_size = snd_pcm_lib_buffer_bytes(substream); - if (state.residue > 0 && state.residue <= buf_size) - pos = buf_size - state.residue; + buffer_tail = rpmsg->recv_msg.param.buffer_offset / + snd_pcm_lib_period_bytes(substream); + pos = buffer_tail * snd_pcm_lib_period_bytes(substream); return bytes_to_frames(substream->runtime, pos); } +static void imx_rpmsg_timer_callback(unsigned long data) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg; + u8 index = i2s_info->work_index; + int time_msec; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_POINTER; + else + rpmsg->send_msg.header.cmd = I2S_RX_POINTER; + + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; + + if (rpmsg_i2s->force_lpa) { + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); + } +} + static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) { int ret = 0; @@ -123,15 +156,39 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; struct dmaengine_pcm_runtime_data *prtd; + int cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_OPEN]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_OPEN]; + + imx_rpmsg_pcm_hardware.buffer_bytes_max = + i2s_info->prealloc_buffer_size; + imx_rpmsg_pcm_hardware.period_bytes_max = + imx_rpmsg_pcm_hardware.buffer_bytes_max / 2; snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_OPEN; + rpmsg->send_msg.header.cmd = I2S_TX_OPEN; else - rpmsg->header.cmd = I2S_RX_OPEN; + rpmsg->send_msg.header.cmd = I2S_RX_OPEN; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0; + } else { + cmd = I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0; + } + i2s_info->send_message(rpmsg, i2s_info); prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); @@ -145,6 +202,11 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) return ret; + + /*create thread*/ + setup_timer(&i2s_info->stream_timer[substream->stream], + imx_rpmsg_timer_callback, (unsigned long)substream); + return ret; } @@ -155,23 +217,51 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_CLOSE; + rpmsg = &i2s_info->rpmsg[I2S_TX_CLOSE]; else - rpmsg->header.cmd = I2S_RX_CLOSE; + rpmsg = &i2s_info->rpmsg[I2S_RX_CLOSE]; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_CLOSE; + else + rpmsg->send_msg.header.cmd = I2S_RX_CLOSE; flush_workqueue(i2s_info->rpmsg_wq); i2s_info->send_message(rpmsg, i2s_info); + del_timer(&i2s_info->stream_timer[substream->stream]); + kfree(prtd); return ret; } +static int imx_rpmsg_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + + /* NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts + * four condition to determine the lpa is enabled. + */ + if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) && + (substream->f_flags & O_NONBLOCK) && + rpmsg_i2s->version == 2 && + rpmsg_i2s->enable_lpa) + rpmsg_i2s->force_lpa = 1; + else + rpmsg_i2s->force_lpa = 0; + + return 0; +} + static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { @@ -186,12 +276,23 @@ static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, static void imx_rpmsg_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; - struct dmaengine_pcm_runtime_data *prtd - = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg, *rpmsg2; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + rpmsg2 = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + } else { + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + rpmsg2 = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + } - prtd->pos += snd_pcm_lib_period_bytes(substream); - if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) - prtd->pos = 0; + rpmsg->recv_msg.param.buffer_offset = + rpmsg2->recv_msg.param.buffer_tail + * snd_pcm_lib_period_bytes(substream); snd_pcm_period_elapsed(substream); } @@ -202,24 +303,31 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_BUFFER; + rpmsg = &i2s_info->rpmsg[I2S_TX_BUFFER]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_BUFFER]; + + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_BUFFER; else - rpmsg->header.cmd = I2S_RX_BUFFER; + rpmsg->send_msg.header.cmd = I2S_RX_BUFFER; - rpmsg->param.buffer_addr = substream->runtime->dma_addr; - rpmsg->param.buffer_size = snd_pcm_lib_buffer_bytes(substream); - rpmsg->param.period_size = snd_pcm_lib_period_bytes(substream); - rpmsg->param.buffer_tail = 0; + rpmsg->send_msg.param.buffer_addr = substream->runtime->dma_addr; + rpmsg->send_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream); + rpmsg->send_msg.param.period_size = snd_pcm_lib_period_bytes(substream); + rpmsg->send_msg.param.buffer_tail = 0; i2s_info->num_period[substream->stream] = - rpmsg->param.buffer_size/rpmsg->param.period_size; + rpmsg->send_msg.param.buffer_size / + rpmsg->send_msg.param.period_size; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; @@ -231,41 +339,60 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; + int time_msec; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_START; + rpmsg = &i2s_info->rpmsg[I2S_TX_START]; else - rpmsg->header.cmd = I2S_RX_START; + rpmsg = &i2s_info->rpmsg[I2S_RX_START]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_START; + else + rpmsg->send_msg.header.cmd = I2S_RX_START; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; + + if (rpmsg_i2s->force_lpa) { + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); + } } -static int imx_rpmsg_resume(struct snd_pcm_substream *substream) +static int imx_rpmsg_restart(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_RESTART; + rpmsg = &i2s_info->rpmsg[I2S_TX_RESTART]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_RESTART]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_RESTART; else - rpmsg->header.cmd = I2S_RX_RESTART; + rpmsg->send_msg.header.cmd = I2S_RX_RESTART; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; @@ -279,20 +406,24 @@ static int imx_rpmsg_pause(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_PAUSE; + rpmsg = &i2s_info->rpmsg[I2S_TX_PAUSE]; else - rpmsg->header.cmd = I2S_RX_PAUSE; + rpmsg = &i2s_info->rpmsg[I2S_RX_PAUSE]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_PAUSE; + else + rpmsg->send_msg.header.cmd = I2S_RX_PAUSE; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; - return 0; } @@ -302,26 +433,35 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_TERMINATE; + rpmsg = &i2s_info->rpmsg[I2S_TX_TERMINATE]; else - rpmsg->header.cmd = I2S_RX_TERMINATE; + rpmsg = &i2s_info->rpmsg[I2S_RX_TERMINATE]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_TERMINATE; + else + rpmsg->send_msg.header.cmd = I2S_RX_TERMINATE; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; + del_timer(&i2s_info->stream_timer[substream->stream]); return 0; } int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); int ret; switch (cmd) { @@ -332,14 +472,18 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) imx_rpmsg_async_issue_pending(substream); break; case SNDRV_PCM_TRIGGER_RESUME: + if (rpmsg_i2s->force_lpa) + break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - imx_rpmsg_resume(substream); + imx_rpmsg_restart(substream); break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (runtime->info & SNDRV_PCM_INFO_PAUSE) - imx_rpmsg_pause(substream); - else - imx_rpmsg_terminate_all(substream); + if (!rpmsg_i2s->force_lpa) { + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + imx_rpmsg_pause(substream); + else + imx_rpmsg_terminate_all(substream); + } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: imx_rpmsg_pause(substream); @@ -354,6 +498,50 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +int imx_rpmsg_pcm_ack(struct snd_pcm_substream *substream) +{ + /*send the hw_avail size through rpmsg*/ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg; + u8 index = i2s_info->work_index; + int buffer_tail = 0; + + if (!rpmsg_i2s->force_lpa) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_PERIOD_DONE; + else + rpmsg->send_msg.header.cmd = I2S_RX_PERIOD_DONE; + + rpmsg->send_msg.header.type = I2S_TYPE_C; + + buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) % + snd_pcm_lib_buffer_bytes(substream)); + buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream); + + if (buffer_tail != rpmsg->send_msg.param.buffer_tail) { + rpmsg->send_msg.param.buffer_tail = buffer_tail; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, + &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; + } + + return 0; +} + static struct snd_pcm_ops imx_rpmsg_pcm_ops = { .open = imx_rpmsg_pcm_open, .close = imx_rpmsg_pcm_close, @@ -363,14 +551,15 @@ static struct snd_pcm_ops imx_rpmsg_pcm_ops = { .trigger = imx_rpmsg_pcm_trigger, .pointer = imx_rpmsg_pcm_pointer, .mmap = imx_rpmsg_pcm_mmap, + .ack = imx_rpmsg_pcm_ack, + .prepare = imx_rpmsg_pcm_prepare, }; static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) + int stream, int size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = imx_rpmsg_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -410,6 +599,9 @@ static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; int ret; ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); @@ -418,14 +610,16 @@ static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd) if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + i2s_info->prealloc_buffer_size); if (ret) goto out; } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + i2s_info->prealloc_buffer_size); if (ret) goto out; } @@ -458,24 +652,41 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) { struct i2s_rpmsg_r *msg = (struct i2s_rpmsg_r *)data; - struct i2s_rpmsg_s *rpmsg; + struct i2s_rpmsg *rpmsg; unsigned long flags; - dev_dbg(&rpdev->dev, "get from%d: cmd:%d.\n", src, msg->header.cmd); + dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n", + src, msg->header.cmd, msg->param.resp); if (msg->header.type == I2S_TYPE_C) { if (msg->header.cmd == I2S_TX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[0], flags); - rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_TX]; - rpmsg->param.buffer_tail++; - rpmsg->param.buffer_tail %= i2s_info_g->num_period[0]; + rpmsg = &i2s_info_g->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (msg->header.major == 1 && msg->header.minor == 2) + rpmsg->recv_msg.param.buffer_tail = + msg->param.buffer_tail; + else + rpmsg->recv_msg.param.buffer_tail++; + + rpmsg->recv_msg.param.buffer_tail %= + i2s_info_g->num_period[0]; + spin_unlock_irqrestore(&i2s_info_g->lock[0], flags); i2s_info_g->callback[0](i2s_info_g->callback_param[0]); + } else if (msg->header.cmd == I2S_RX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[1], flags); - rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_RX]; - rpmsg->param.buffer_tail++; - rpmsg->param.buffer_tail %= i2s_info_g->num_period[1]; + rpmsg = &i2s_info_g->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (msg->header.major == 1 && msg->header.minor == 2) + rpmsg->recv_msg.param.buffer_tail = + msg->param.buffer_tail; + else + rpmsg->recv_msg.param.buffer_tail++; + + rpmsg->recv_msg.param.buffer_tail %= + i2s_info_g->num_period[1]; spin_unlock_irqrestore(&i2s_info_g->lock[1], flags); i2s_info_g->callback[1](i2s_info_g->callback_param[1]); } @@ -495,6 +706,9 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) struct fsl_rpmsg_i2s *rpmsg_i2s = NULL; int ret; + if (!i2s_info_g) + return 0; + i2s_info_g->rpdev = rpdev; init_completion(&i2s_info_g->cmd_complete); @@ -504,13 +718,18 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) rpmsg_i2s = container_of(i2s_info_g, struct fsl_rpmsg_i2s, i2s_info); - codec_pdev = platform_device_register_data(&rpmsg_i2s->pdev->dev, - RPMSG_CODEC_DRV_NAME, PLATFORM_DEVID_NONE, - NULL, 0); - if (IS_ERR(codec_pdev)) { - dev_err(&rpdev->dev, "failed to register rpmsg audio codec\n"); - ret = PTR_ERR(codec_pdev); - return ret; + if (rpmsg_i2s->codec) { + codec_pdev = platform_device_register_data( + &rpmsg_i2s->pdev->dev, + RPMSG_CODEC_DRV_NAME, + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(codec_pdev)) { + dev_err(&rpdev->dev, + "failed to register rpmsg audio codec\n"); + ret = PTR_ERR(codec_pdev); + return ret; + } } return 0; |