1858f7a5cSDaniel Baluta // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2858f7a5cSDaniel Baluta // 3858f7a5cSDaniel Baluta // Copyright 2021 NXP 4858f7a5cSDaniel Baluta // 5858f7a5cSDaniel Baluta // Author: Daniel Baluta <daniel.baluta@nxp.com> 6858f7a5cSDaniel Baluta 7858f7a5cSDaniel Baluta #include <sound/soc.h> 8858f7a5cSDaniel Baluta #include <sound/sof.h> 9858f7a5cSDaniel Baluta #include <sound/compress_driver.h> 10858f7a5cSDaniel Baluta #include "sof-audio.h" 11858f7a5cSDaniel Baluta #include "sof-priv.h" 12858f7a5cSDaniel Baluta 13*6324cf90SDaniel Baluta static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp, 14*6324cf90SDaniel Baluta u64 host_pos, u64 buffer_size) 15*6324cf90SDaniel Baluta { 16*6324cf90SDaniel Baluta u64 prev_pos; 17*6324cf90SDaniel Baluta unsigned int copied; 18*6324cf90SDaniel Baluta 19*6324cf90SDaniel Baluta div64_u64_rem(tstamp->copied_total, buffer_size, &prev_pos); 20*6324cf90SDaniel Baluta 21*6324cf90SDaniel Baluta if (host_pos < prev_pos) 22*6324cf90SDaniel Baluta copied = (buffer_size - prev_pos) + host_pos; 23*6324cf90SDaniel Baluta else 24*6324cf90SDaniel Baluta copied = host_pos - prev_pos; 25*6324cf90SDaniel Baluta 26*6324cf90SDaniel Baluta tstamp->copied_total += copied; 27*6324cf90SDaniel Baluta } 28*6324cf90SDaniel Baluta 29858f7a5cSDaniel Baluta static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work) 30858f7a5cSDaniel Baluta { 31858f7a5cSDaniel Baluta struct snd_sof_pcm_stream *sps = 32858f7a5cSDaniel Baluta container_of(work, struct snd_sof_pcm_stream, 33858f7a5cSDaniel Baluta period_elapsed_work); 34858f7a5cSDaniel Baluta 35858f7a5cSDaniel Baluta snd_compr_fragment_elapsed(sps->cstream); 36858f7a5cSDaniel Baluta } 37858f7a5cSDaniel Baluta 38858f7a5cSDaniel Baluta void snd_sof_compr_init_elapsed_work(struct work_struct *work) 39858f7a5cSDaniel Baluta { 40858f7a5cSDaniel Baluta INIT_WORK(work, snd_sof_compr_fragment_elapsed_work); 41858f7a5cSDaniel Baluta } 42858f7a5cSDaniel Baluta 43858f7a5cSDaniel Baluta /* 44858f7a5cSDaniel Baluta * sof compr fragment elapse, this could be called in irq thread context 45858f7a5cSDaniel Baluta */ 46858f7a5cSDaniel Baluta void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) 47858f7a5cSDaniel Baluta { 48*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 49*6324cf90SDaniel Baluta struct snd_compr_runtime *crtd = cstream->runtime; 50858f7a5cSDaniel Baluta struct snd_soc_component *component; 51*6324cf90SDaniel Baluta struct snd_compr_tstamp *tstamp; 52858f7a5cSDaniel Baluta struct snd_sof_pcm *spcm; 53858f7a5cSDaniel Baluta 54858f7a5cSDaniel Baluta if (!cstream) 55858f7a5cSDaniel Baluta return; 56858f7a5cSDaniel Baluta 57*6324cf90SDaniel Baluta tstamp = crtd->private_data; 58858f7a5cSDaniel Baluta component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 59858f7a5cSDaniel Baluta 60858f7a5cSDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 61858f7a5cSDaniel Baluta if (!spcm) { 62858f7a5cSDaniel Baluta dev_err(component->dev, 63858f7a5cSDaniel Baluta "fragment elapsed called for unknown stream!\n"); 64858f7a5cSDaniel Baluta return; 65858f7a5cSDaniel Baluta } 66858f7a5cSDaniel Baluta 67*6324cf90SDaniel Baluta sof_set_transferred_bytes(tstamp, spcm->stream[cstream->direction].posn.host_posn, 68*6324cf90SDaniel Baluta crtd->buffer_size); 69*6324cf90SDaniel Baluta 70858f7a5cSDaniel Baluta /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ 71858f7a5cSDaniel Baluta schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); 72858f7a5cSDaniel Baluta } 73*6324cf90SDaniel Baluta 74*6324cf90SDaniel Baluta static int create_page_table(struct snd_soc_component *component, 75*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, 76*6324cf90SDaniel Baluta unsigned char *dma_area, size_t size) 77*6324cf90SDaniel Baluta { 78*6324cf90SDaniel Baluta struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p; 79*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 80*6324cf90SDaniel Baluta int dir = cstream->direction; 81*6324cf90SDaniel Baluta struct snd_sof_pcm *spcm; 82*6324cf90SDaniel Baluta 83*6324cf90SDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 84*6324cf90SDaniel Baluta if (!spcm) 85*6324cf90SDaniel Baluta return -EINVAL; 86*6324cf90SDaniel Baluta 87*6324cf90SDaniel Baluta return snd_sof_create_page_table(component->dev, dmab, 88*6324cf90SDaniel Baluta spcm->stream[dir].page_table.area, size); 89*6324cf90SDaniel Baluta } 90*6324cf90SDaniel Baluta 91*6324cf90SDaniel Baluta int sof_compr_open(struct snd_soc_component *component, 92*6324cf90SDaniel Baluta struct snd_compr_stream *cstream) 93*6324cf90SDaniel Baluta { 94*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 95*6324cf90SDaniel Baluta struct snd_compr_runtime *crtd = cstream->runtime; 96*6324cf90SDaniel Baluta struct snd_compr_tstamp *tstamp; 97*6324cf90SDaniel Baluta struct snd_sof_pcm *spcm; 98*6324cf90SDaniel Baluta int dir; 99*6324cf90SDaniel Baluta 100*6324cf90SDaniel Baluta tstamp = kzalloc(sizeof(*tstamp), GFP_KERNEL); 101*6324cf90SDaniel Baluta if (!tstamp) 102*6324cf90SDaniel Baluta return -ENOMEM; 103*6324cf90SDaniel Baluta 104*6324cf90SDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 105*6324cf90SDaniel Baluta if (!spcm) { 106*6324cf90SDaniel Baluta kfree(tstamp); 107*6324cf90SDaniel Baluta return -EINVAL; 108*6324cf90SDaniel Baluta } 109*6324cf90SDaniel Baluta 110*6324cf90SDaniel Baluta dir = cstream->direction; 111*6324cf90SDaniel Baluta 112*6324cf90SDaniel Baluta if (spcm->stream[dir].cstream) { 113*6324cf90SDaniel Baluta kfree(tstamp); 114*6324cf90SDaniel Baluta return -EBUSY; 115*6324cf90SDaniel Baluta } 116*6324cf90SDaniel Baluta 117*6324cf90SDaniel Baluta spcm->stream[dir].cstream = cstream; 118*6324cf90SDaniel Baluta spcm->stream[dir].posn.host_posn = 0; 119*6324cf90SDaniel Baluta spcm->stream[dir].posn.dai_posn = 0; 120*6324cf90SDaniel Baluta spcm->prepared[dir] = false; 121*6324cf90SDaniel Baluta 122*6324cf90SDaniel Baluta crtd->private_data = tstamp; 123*6324cf90SDaniel Baluta 124*6324cf90SDaniel Baluta return 0; 125*6324cf90SDaniel Baluta } 126*6324cf90SDaniel Baluta 127*6324cf90SDaniel Baluta int sof_compr_free(struct snd_soc_component *component, 128*6324cf90SDaniel Baluta struct snd_compr_stream *cstream) 129*6324cf90SDaniel Baluta { 130*6324cf90SDaniel Baluta struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 131*6324cf90SDaniel Baluta struct snd_compr_tstamp *tstamp = cstream->runtime->private_data; 132*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 133*6324cf90SDaniel Baluta struct sof_ipc_stream stream; 134*6324cf90SDaniel Baluta struct sof_ipc_reply reply; 135*6324cf90SDaniel Baluta struct snd_sof_pcm *spcm; 136*6324cf90SDaniel Baluta int ret = 0; 137*6324cf90SDaniel Baluta 138*6324cf90SDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 139*6324cf90SDaniel Baluta if (!spcm) 140*6324cf90SDaniel Baluta return -EINVAL; 141*6324cf90SDaniel Baluta 142*6324cf90SDaniel Baluta stream.hdr.size = sizeof(stream); 143*6324cf90SDaniel Baluta stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; 144*6324cf90SDaniel Baluta stream.comp_id = spcm->stream[cstream->direction].comp_id; 145*6324cf90SDaniel Baluta 146*6324cf90SDaniel Baluta if (spcm->prepared[cstream->direction]) { 147*6324cf90SDaniel Baluta ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, 148*6324cf90SDaniel Baluta &stream, sizeof(stream), 149*6324cf90SDaniel Baluta &reply, sizeof(reply)); 150*6324cf90SDaniel Baluta if (!ret) 151*6324cf90SDaniel Baluta spcm->prepared[cstream->direction] = false; 152*6324cf90SDaniel Baluta } 153*6324cf90SDaniel Baluta 154*6324cf90SDaniel Baluta cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work); 155*6324cf90SDaniel Baluta spcm->stream[cstream->direction].cstream = NULL; 156*6324cf90SDaniel Baluta kfree(tstamp); 157*6324cf90SDaniel Baluta 158*6324cf90SDaniel Baluta return ret; 159*6324cf90SDaniel Baluta } 160*6324cf90SDaniel Baluta 161*6324cf90SDaniel Baluta int sof_compr_set_params(struct snd_soc_component *component, 162*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, struct snd_compr_params *params) 163*6324cf90SDaniel Baluta { 164*6324cf90SDaniel Baluta struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 165*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 166*6324cf90SDaniel Baluta struct snd_compr_runtime *crtd = cstream->runtime; 167*6324cf90SDaniel Baluta struct sof_ipc_pcm_params_reply ipc_params_reply; 168*6324cf90SDaniel Baluta struct snd_compr_tstamp *tstamp; 169*6324cf90SDaniel Baluta struct sof_ipc_pcm_params pcm; 170*6324cf90SDaniel Baluta struct snd_sof_pcm *spcm; 171*6324cf90SDaniel Baluta int ret; 172*6324cf90SDaniel Baluta 173*6324cf90SDaniel Baluta tstamp = crtd->private_data; 174*6324cf90SDaniel Baluta 175*6324cf90SDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 176*6324cf90SDaniel Baluta 177*6324cf90SDaniel Baluta if (!spcm) 178*6324cf90SDaniel Baluta return -EINVAL; 179*6324cf90SDaniel Baluta 180*6324cf90SDaniel Baluta cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; 181*6324cf90SDaniel Baluta cstream->dma_buffer.dev.dev = sdev->dev; 182*6324cf90SDaniel Baluta ret = snd_compr_malloc_pages(cstream, crtd->buffer_size); 183*6324cf90SDaniel Baluta if (ret < 0) 184*6324cf90SDaniel Baluta return ret; 185*6324cf90SDaniel Baluta 186*6324cf90SDaniel Baluta ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes); 187*6324cf90SDaniel Baluta if (ret < 0) 188*6324cf90SDaniel Baluta return ret; 189*6324cf90SDaniel Baluta 190*6324cf90SDaniel Baluta memset(&pcm, 0, sizeof(pcm)); 191*6324cf90SDaniel Baluta 192*6324cf90SDaniel Baluta pcm.params.buffer.pages = PFN_UP(crtd->dma_bytes); 193*6324cf90SDaniel Baluta pcm.hdr.size = sizeof(pcm); 194*6324cf90SDaniel Baluta pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; 195*6324cf90SDaniel Baluta 196*6324cf90SDaniel Baluta pcm.comp_id = spcm->stream[cstream->direction].comp_id; 197*6324cf90SDaniel Baluta pcm.params.hdr.size = sizeof(pcm.params); 198*6324cf90SDaniel Baluta pcm.params.buffer.phy_addr = spcm->stream[cstream->direction].page_table.addr; 199*6324cf90SDaniel Baluta pcm.params.buffer.size = crtd->dma_bytes; 200*6324cf90SDaniel Baluta pcm.params.direction = cstream->direction; 201*6324cf90SDaniel Baluta pcm.params.channels = params->codec.ch_out; 202*6324cf90SDaniel Baluta pcm.params.rate = params->codec.sample_rate; 203*6324cf90SDaniel Baluta pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; 204*6324cf90SDaniel Baluta pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; 205*6324cf90SDaniel Baluta pcm.params.sample_container_bytes = 206*6324cf90SDaniel Baluta snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3; 207*6324cf90SDaniel Baluta pcm.params.host_period_bytes = params->buffer.fragment_size; 208*6324cf90SDaniel Baluta 209*6324cf90SDaniel Baluta ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), 210*6324cf90SDaniel Baluta &ipc_params_reply, sizeof(ipc_params_reply)); 211*6324cf90SDaniel Baluta if (ret < 0) { 212*6324cf90SDaniel Baluta dev_err(component->dev, "error ipc failed\n"); 213*6324cf90SDaniel Baluta return ret; 214*6324cf90SDaniel Baluta } 215*6324cf90SDaniel Baluta 216*6324cf90SDaniel Baluta tstamp->byte_offset = sdev->stream_box.offset + ipc_params_reply.posn_offset; 217*6324cf90SDaniel Baluta tstamp->sampling_rate = params->codec.sample_rate; 218*6324cf90SDaniel Baluta 219*6324cf90SDaniel Baluta spcm->prepared[cstream->direction] = true; 220*6324cf90SDaniel Baluta 221*6324cf90SDaniel Baluta return 0; 222*6324cf90SDaniel Baluta } 223*6324cf90SDaniel Baluta 224*6324cf90SDaniel Baluta int sof_compr_get_params(struct snd_soc_component *component, 225*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, struct snd_codec *params) 226*6324cf90SDaniel Baluta { 227*6324cf90SDaniel Baluta /* TODO: we don't query the supported codecs for now, if the 228*6324cf90SDaniel Baluta * application asks for an unsupported codec the set_params() will fail. 229*6324cf90SDaniel Baluta */ 230*6324cf90SDaniel Baluta return 0; 231*6324cf90SDaniel Baluta } 232*6324cf90SDaniel Baluta 233*6324cf90SDaniel Baluta int sof_compr_trigger(struct snd_soc_component *component, 234*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, int cmd) 235*6324cf90SDaniel Baluta { 236*6324cf90SDaniel Baluta struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 237*6324cf90SDaniel Baluta struct snd_soc_pcm_runtime *rtd = cstream->private_data; 238*6324cf90SDaniel Baluta struct sof_ipc_stream stream; 239*6324cf90SDaniel Baluta struct sof_ipc_reply reply; 240*6324cf90SDaniel Baluta struct snd_sof_pcm *spcm; 241*6324cf90SDaniel Baluta 242*6324cf90SDaniel Baluta spcm = snd_sof_find_spcm_dai(component, rtd); 243*6324cf90SDaniel Baluta if (!spcm) 244*6324cf90SDaniel Baluta return -EINVAL; 245*6324cf90SDaniel Baluta 246*6324cf90SDaniel Baluta stream.hdr.size = sizeof(stream); 247*6324cf90SDaniel Baluta stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; 248*6324cf90SDaniel Baluta stream.comp_id = spcm->stream[cstream->direction].comp_id; 249*6324cf90SDaniel Baluta 250*6324cf90SDaniel Baluta switch (cmd) { 251*6324cf90SDaniel Baluta case SNDRV_PCM_TRIGGER_START: 252*6324cf90SDaniel Baluta stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; 253*6324cf90SDaniel Baluta break; 254*6324cf90SDaniel Baluta case SNDRV_PCM_TRIGGER_STOP: 255*6324cf90SDaniel Baluta stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; 256*6324cf90SDaniel Baluta break; 257*6324cf90SDaniel Baluta case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 258*6324cf90SDaniel Baluta stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; 259*6324cf90SDaniel Baluta break; 260*6324cf90SDaniel Baluta case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 261*6324cf90SDaniel Baluta stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; 262*6324cf90SDaniel Baluta break; 263*6324cf90SDaniel Baluta default: 264*6324cf90SDaniel Baluta dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd); 265*6324cf90SDaniel Baluta break; 266*6324cf90SDaniel Baluta } 267*6324cf90SDaniel Baluta 268*6324cf90SDaniel Baluta return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, 269*6324cf90SDaniel Baluta &stream, sizeof(stream), 270*6324cf90SDaniel Baluta &reply, sizeof(reply)); 271*6324cf90SDaniel Baluta } 272*6324cf90SDaniel Baluta 273*6324cf90SDaniel Baluta int sof_compr_copy(struct snd_soc_component *component, 274*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, 275*6324cf90SDaniel Baluta char __user *buf, size_t count) 276*6324cf90SDaniel Baluta { 277*6324cf90SDaniel Baluta struct snd_compr_runtime *rtd = cstream->runtime; 278*6324cf90SDaniel Baluta unsigned int offset, n; 279*6324cf90SDaniel Baluta void *ptr; 280*6324cf90SDaniel Baluta int ret; 281*6324cf90SDaniel Baluta 282*6324cf90SDaniel Baluta if (count > rtd->buffer_size) 283*6324cf90SDaniel Baluta count = rtd->buffer_size; 284*6324cf90SDaniel Baluta 285*6324cf90SDaniel Baluta div_u64_rem(rtd->total_bytes_available, rtd->buffer_size, &offset); 286*6324cf90SDaniel Baluta ptr = rtd->dma_area + offset; 287*6324cf90SDaniel Baluta n = rtd->buffer_size - offset; 288*6324cf90SDaniel Baluta 289*6324cf90SDaniel Baluta if (count < n) { 290*6324cf90SDaniel Baluta ret = copy_from_user(ptr, buf, count); 291*6324cf90SDaniel Baluta } else { 292*6324cf90SDaniel Baluta ret = copy_from_user(ptr, buf, n); 293*6324cf90SDaniel Baluta ret += copy_from_user(rtd->dma_area, buf + n, count - n); 294*6324cf90SDaniel Baluta } 295*6324cf90SDaniel Baluta 296*6324cf90SDaniel Baluta return count - ret; 297*6324cf90SDaniel Baluta } 298*6324cf90SDaniel Baluta 299*6324cf90SDaniel Baluta static int sof_compr_pointer(struct snd_soc_component *component, 300*6324cf90SDaniel Baluta struct snd_compr_stream *cstream, 301*6324cf90SDaniel Baluta struct snd_compr_tstamp *tstamp) 302*6324cf90SDaniel Baluta { 303*6324cf90SDaniel Baluta struct snd_compr_tstamp *pstamp = cstream->runtime->private_data; 304*6324cf90SDaniel Baluta 305*6324cf90SDaniel Baluta tstamp->sampling_rate = pstamp->sampling_rate; 306*6324cf90SDaniel Baluta tstamp->copied_total = pstamp->copied_total; 307*6324cf90SDaniel Baluta 308*6324cf90SDaniel Baluta return 0; 309*6324cf90SDaniel Baluta } 310*6324cf90SDaniel Baluta 311*6324cf90SDaniel Baluta struct snd_compress_ops sof_compressed_ops = { 312*6324cf90SDaniel Baluta .open = sof_compr_open, 313*6324cf90SDaniel Baluta .free = sof_compr_free, 314*6324cf90SDaniel Baluta .set_params = sof_compr_set_params, 315*6324cf90SDaniel Baluta .get_params = sof_compr_get_params, 316*6324cf90SDaniel Baluta .trigger = sof_compr_trigger, 317*6324cf90SDaniel Baluta .pointer = sof_compr_pointer, 318*6324cf90SDaniel Baluta .copy = sof_compr_copy, 319*6324cf90SDaniel Baluta }; 320*6324cf90SDaniel Baluta EXPORT_SYMBOL(sof_compressed_ops); 321