// SPDX-License-Identifier: GPL-2.0+ // // DSP driver compress implementation // // Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED. // Copyright 2018 NXP #include #include #include #include #include "fsl_dsp.h" #include "fsl_dsp_platform.h" #include "fsl_dsp_xaf_api.h" #define NUM_CODEC 2 #define MIN_FRAGMENT 1 #define MAX_FRAGMENT 1 #define MIN_FRAGMENT_SIZE (4 * 1024) #define MAX_FRAGMENT_SIZE (4 * 1024) void dsp_platform_process(struct work_struct *w) { struct xf_client *client = container_of(w, struct xf_client, work); struct xf_proxy *proxy = client->proxy; struct xf_message *rmsg; while (1) { rmsg = xf_cmd_recv(proxy, &client->wait, &client->queue, 1); if (!proxy->is_active || IS_ERR(rmsg)) return; if (rmsg->opcode == XF_EMPTY_THIS_BUFFER) { client->consume_bytes += rmsg->length; snd_compr_fragment_elapsed(client->cstream); if (rmsg->buffer == NULL && rmsg->length == 0) snd_compr_drain_notify(client->cstream); } else { memcpy(&client->m, rmsg, sizeof(struct xf_message)); complete(&client->compr_complete); } xf_msg_free(proxy, rmsg); xf_unlock(&proxy->lock); } } static int dsp_platform_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; drv->client = xf_client_alloc(dsp_priv); if (IS_ERR(drv->client)) return PTR_ERR(drv->client); fsl_dsp_open_func(dsp_priv, drv->client); drv->client->proxy = &dsp_priv->proxy; cpu_dai->driver->ops->startup(NULL, cpu_dai); drv->client->cstream = cstream; INIT_WORK(&drv->client->work, dsp_platform_process); return 0; } static int dsp_platform_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; int ret; if (cstream->runtime->state != SNDRV_PCM_STATE_PAUSED && cstream->runtime->state != SNDRV_PCM_STATE_RUNNING && cstream->runtime->state != SNDRV_PCM_STATE_DRAINING) { ret = xaf_comp_delete(drv->client, &drv->component[1]); if (ret) { dev_err(component->dev, "Fail to delete component, err = %d\n", ret); return ret; } ret = xaf_comp_delete(drv->client, &drv->component[0]); if (ret) { dev_err(component->dev, "Fail to delete component, err = %d\n", ret); return ret; } } cpu_dai->driver->ops->shutdown(NULL, cpu_dai); drv->client->proxy->is_active = 0; wake_up(&drv->client->wait); cancel_work_sync(&drv->client->work); fsl_dsp_close_func(drv->client); return 0; } static int dsp_platform_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { /* accroding to the params, load the library and create component*/ struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; struct xf_proxy *p_proxy = &dsp_priv->proxy; struct xf_set_param_msg s_param; int ret; switch (params->codec.id) { case SND_AUDIOCODEC_MP3: drv->codec_type = CODEC_MP3_DEC; break; case SND_AUDIOCODEC_AAC: drv->codec_type = CODEC_AAC_DEC; break; default: dev_err(component->dev, "codec not supported, id =%d\n", params->codec.id); return -EINVAL; } /* ...create auxiliary buffers pool for control commands */ ret = xf_pool_alloc(drv->client, p_proxy, XA_AUX_POOL_SIZE, XA_AUX_POOL_MSG_LENGTH, XF_POOL_AUX, &p_proxy->aux); if (ret) { dev_err(component->dev, "xf_pool_alloc failed"); return ret; } /* ...create pipeline */ ret = xaf_pipeline_create(&drv->pipeline); if (ret) { dev_err(component->dev, "create pipeline error\n"); goto err_pool_alloc; } /* ...create component */ ret = xaf_comp_create(drv->client, p_proxy, &drv->component[0], drv->codec_type); if (ret) { dev_err(component->dev, "create component failed type = %d, err = %d\n", drv->codec_type, ret); goto err_pool_alloc; } if (sysfs_streq(dsp_priv->audio_iface, "sai")) drv->renderer_type = RENDER_SAI; else drv->renderer_type = RENDER_ESAI; ret = xaf_comp_create(drv->client, p_proxy, &drv->component[1], drv->renderer_type); if (ret) { dev_err(component->dev, "create component failed, type = %d, err = %d\n", drv->renderer_type, ret); goto err_comp0_create; } /* ...add component into pipeline */ ret = xaf_comp_add(&drv->pipeline, &drv->component[0]); if (ret) { dev_err(component->dev, "add component failed, type = %d, err = %d\n", drv->codec_type, ret); goto err_comp0_create; } ret = xaf_comp_add(&drv->pipeline, &drv->component[1]); if (ret) { dev_err(component->dev, "add component failed, type = %d, err = %d\n", drv->renderer_type, ret); goto err_comp1_create; } drv->client->input_bytes = 0; drv->client->consume_bytes = 0; s_param.id = XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE; s_param.mixData.value = params->codec.sample_rate; ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); if (ret) { dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n", s_param.id, s_param.mixData.value, ret); goto err_comp1_create; } s_param.id = XA_RENDERER_CONFIG_PARAM_CHANNELS; s_param.mixData.value = params->codec.ch_out; ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); if (ret) { dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n", s_param.id, s_param.mixData.value, ret); goto err_comp1_create; } s_param.id = XA_RENDERER_CONFIG_PARAM_PCM_WIDTH; s_param.mixData.value = 16; ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param); if (ret) { dev_err(component->dev, "set param[cmd:0x%x|val:0x%x] error, err = %d\n", s_param.id, s_param.mixData.value, ret); goto err_comp1_create; } return 0; err_comp1_create: xaf_comp_delete(drv->client, &drv->component[1]); err_comp0_create: xaf_comp_delete(drv->client, &drv->component[0]); err_pool_alloc: xf_pool_free(drv->client, p_proxy->aux); return ret; } static int dsp_platform_compr_trigger_start(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; struct xaf_comp *p_comp = &drv->component[0]; int ret; ret = xaf_comp_process(drv->client, p_comp, p_comp->inptr, drv->client->input_bytes, XF_EMPTY_THIS_BUFFER); ret = xaf_connect(drv->client, &drv->component[0], &drv->component[1], 1, OUTBUF_SIZE); if (ret) { dev_err(component->dev, "Failed to connect component, err = %d\n", ret); return ret; } schedule_work(&drv->client->work); return 0; } static int dsp_platform_compr_trigger_stop(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; int ret; ret = xaf_comp_flush(drv->client, &drv->component[0]); if (ret) { dev_err(component->dev, "Fail to flush component, err = %d\n", ret); return ret; } ret = xaf_comp_flush(drv->client, &drv->component[1]); if (ret) { dev_err(component->dev, "Fail to flush component, err = %d\n", ret); return ret; } ret = xaf_comp_delete(drv->client, &drv->component[0]); if (ret) { dev_err(component->dev, "Fail to delete component, err = %d\n", ret); return ret; } ret = xaf_comp_delete(drv->client, &drv->component[1]); if (ret) { dev_err(component->dev, "Fail to delete component, err = %d\n", ret); return ret; } return 0; } static int dsp_platform_compr_trigger_drain(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; struct xaf_comp *p_comp = &drv->component[0]; int ret; ret = xaf_comp_process(drv->client, p_comp, NULL, 0, XF_EMPTY_THIS_BUFFER); schedule_work(&drv->client->work); return 0; } static int dsp_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd) { int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: ret = dsp_platform_compr_trigger_start(cstream); break; case SNDRV_PCM_TRIGGER_STOP: ret = dsp_platform_compr_trigger_stop(cstream); break; case SND_COMPR_TRIGGER_DRAIN: ret = dsp_platform_compr_trigger_drain(cstream); break; case SND_COMPR_TRIGGER_PARTIAL_DRAIN: break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: break; } /*send command*/ return ret; } static int dsp_platform_compr_pointer(struct snd_compr_stream *cstream, struct snd_compr_tstamp *tstamp) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; struct xf_get_param_msg g_param[2]; int ret; g_param[0].id = XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE; g_param[1].id = XA_RENDERER_CONFIG_PARAM_CONSUMED; ret = xaf_comp_get_config(drv->client, &drv->component[1], 2, &g_param); if (ret) { dev_err(component->dev, "get param[cmd:0x%x|val:0x%x] error, err = %d\n", g_param[0].id, g_param[0].mixData.value, ret); goto out; } tstamp->copied_total = drv->client->input_bytes; tstamp->byte_offset = drv->client->input_bytes; tstamp->pcm_frames = 0x900; tstamp->pcm_io_frames = g_param[1].mixData.value; tstamp->sampling_rate = g_param[0].mixData.value; out: return 0; } static int dsp_platform_compr_copy(struct snd_compr_stream *cstream, char __user *buf, size_t count) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME); struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component); struct dsp_data *drv = &dsp_priv->dsp_data; struct xaf_comp *p_comp = &drv->component[0]; int copied = 0; int ret; if (drv->client->input_bytes == drv->client->consume_bytes) { if (count > INBUF_SIZE){ ret = copy_from_user(p_comp->inptr, buf, INBUF_SIZE); if (ret) { dev_err(component->dev, "failed to get message from user space\n"); return -EFAULT; } copied = INBUF_SIZE; } else { ret = copy_from_user(p_comp->inptr, buf, count); if (ret) { dev_err(component->dev, "failed to get message from user space\n"); return -EFAULT; } copied = count; } drv->client->input_bytes += copied; if (cstream->runtime->state == SNDRV_PCM_STATE_RUNNING) { ret = xaf_comp_process(drv->client, p_comp, p_comp->inptr, copied, XF_EMPTY_THIS_BUFFER); schedule_work(&drv->client->work); } } return copied; } static int dsp_platform_compr_get_caps(struct snd_compr_stream *cstream, struct snd_compr_caps *caps) { caps->num_codecs = NUM_CODEC; caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ caps->min_fragments = MIN_FRAGMENT; caps->max_fragments = MAX_FRAGMENT; caps->codecs[0] = SND_AUDIOCODEC_MP3; caps->codecs[1] = SND_AUDIOCODEC_AAC; return 0; } static struct snd_compr_codec_caps caps_mp3 = { .num_descriptors = 1, .descriptor[0].max_ch = 2, .descriptor[0].sample_rates[0] = 48000, .descriptor[0].sample_rates[1] = 44100, .descriptor[0].sample_rates[2] = 32000, .descriptor[0].sample_rates[3] = 16000, .descriptor[0].sample_rates[4] = 8000, .descriptor[0].num_sample_rates = 5, .descriptor[0].bit_rate[0] = 320, .descriptor[0].bit_rate[1] = 192, .descriptor[0].num_bitrates = 2, .descriptor[0].profiles = 0, .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, .descriptor[0].formats = 0, }; static struct snd_compr_codec_caps caps_aac = { .num_descriptors = 2, .descriptor[1].max_ch = 2, .descriptor[0].sample_rates[0] = 48000, .descriptor[0].sample_rates[1] = 44100, .descriptor[0].sample_rates[2] = 32000, .descriptor[0].sample_rates[3] = 16000, .descriptor[0].sample_rates[4] = 8000, .descriptor[0].num_sample_rates = 5, .descriptor[1].bit_rate[0] = 320, .descriptor[1].bit_rate[1] = 192, .descriptor[1].num_bitrates = 2, .descriptor[1].profiles = 0, .descriptor[1].modes = 0, .descriptor[1].formats = (SND_AUDIOSTREAMFORMAT_MP4ADTS | SND_AUDIOSTREAMFORMAT_RAW), }; static int dsp_platform_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_compr_codec_caps *codec) { if (codec->codec == SND_AUDIOCODEC_MP3) *codec = caps_mp3; else if (codec->codec == SND_AUDIOCODEC_AAC) *codec = caps_aac; else return -EINVAL; return 0; } static int dsp_platform_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { return 0; } const struct snd_compr_ops dsp_platform_compr_ops = { .open = dsp_platform_compr_open, .free = dsp_platform_compr_free, .set_params = dsp_platform_compr_set_params, .set_metadata = dsp_platform_compr_set_metadata, .trigger = dsp_platform_compr_trigger, .pointer = dsp_platform_compr_pointer, .copy = dsp_platform_compr_copy, .get_caps = dsp_platform_compr_get_caps, .get_codec_caps = dsp_platform_compr_get_codec_caps, };