root/sound/soc/intel/atom/sst-mfld-platform-compress.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  sst_mfld_platform.c - Intel MID Platform driver
 *
 *  Copyright (C) 2010-2014 Intel Corp
 *  Author: Vinod Koul <vinod.koul@intel.com>
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/slab.h>
#include <linux/io.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
#include <asm/div64.h>
#include "sst-mfld-platform.h"

/* compress stream operations */
static void sst_compr_fragment_elapsed(void *arg)
{
        struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;

        pr_debug("fragment elapsed by driver\n");
        if (cstream)
                snd_compr_fragment_elapsed(cstream);
}

static void sst_drain_notify(void *arg)
{
        struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;

        pr_debug("drain notify by driver\n");
        if (cstream)
                snd_compr_drain_notify(cstream);
}

static int sst_platform_compr_open(struct snd_soc_component *component,
                                   struct snd_compr_stream *cstream)
{
        int ret_val;
        struct snd_compr_runtime *runtime = cstream->runtime;
        struct sst_runtime_stream *stream;

        stream = kzalloc_obj(*stream);
        if (!stream)
                return -ENOMEM;

        spin_lock_init(&stream->status_lock);

        /* get the sst ops */
        if (!sst || !try_module_get(sst->dev->driver->owner)) {
                pr_err("no device available to run\n");
                ret_val = -ENODEV;
                goto out_ops;
        }
        stream->compr_ops = sst->compr_ops;
        stream->id = 0;

        /* Turn on LPE */
        sst->compr_ops->power(sst->dev, true);

        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        runtime->private_data = stream;
        return 0;
out_ops:
        kfree(stream);
        return ret_val;
}

static int sst_platform_compr_free(struct snd_soc_component *component,
                                   struct snd_compr_stream *cstream)
{
        struct sst_runtime_stream *stream;
        int ret_val = 0, str_id;

        stream = cstream->runtime->private_data;
        /* Turn off LPE */
        sst->compr_ops->power(sst->dev, false);

        /*need to check*/
        str_id = stream->id;
        if (str_id)
                ret_val = stream->compr_ops->close(sst->dev, str_id);
        module_put(sst->dev->driver->owner);
        kfree(stream);
        pr_debug("%s: %d\n", __func__, ret_val);
        return 0;
}

static int sst_platform_compr_set_params(struct snd_soc_component *component,
                                         struct snd_compr_stream *cstream,
                                         struct snd_compr_params *params)
{
        struct sst_runtime_stream *stream;
        int retval;
        struct snd_sst_params str_params;
        struct sst_compress_cb cb;
        struct sst_data *ctx = snd_soc_component_get_drvdata(component);

        stream = cstream->runtime->private_data;
        /* construct fw structure for this*/
        memset(&str_params, 0, sizeof(str_params));

        /* fill the device type and stream id to pass to SST driver */
        retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
        pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
        if (retval < 0)
                return retval;

        switch (params->codec.id) {
        case SND_AUDIOCODEC_MP3: {
                str_params.codec = SST_CODEC_TYPE_MP3;
                str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
                str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
                break;
        }

        case SND_AUDIOCODEC_AAC: {
                str_params.codec = SST_CODEC_TYPE_AAC;
                str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
                str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
                if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
                        str_params.sparams.uc.aac_params.bs_format =
                                                        AAC_BIT_STREAM_ADTS;
                else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
                        str_params.sparams.uc.aac_params.bs_format =
                                                        AAC_BIT_STREAM_RAW;
                else {
                        pr_err("Undefined format%d\n", params->codec.format);
                        return -EINVAL;
                }
                str_params.sparams.uc.aac_params.externalsr =
                                                params->codec.sample_rate;
                break;
        }

        default:
                pr_err("codec not supported, id =%d\n", params->codec.id);
                return -EINVAL;
        }

        str_params.aparams.ring_buf_info[0].addr  =
                                        virt_to_phys(cstream->runtime->buffer);
        str_params.aparams.ring_buf_info[0].size =
                                        cstream->runtime->buffer_size;
        str_params.aparams.sg_count = 1;
        str_params.aparams.frag_size = cstream->runtime->fragment_size;

        cb.param = cstream;
        cb.compr_cb = sst_compr_fragment_elapsed;
        cb.drain_cb_param = cstream;
        cb.drain_notify = sst_drain_notify;

        retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
        if (retval < 0) {
                pr_err("stream allocation failed %d\n", retval);
                return retval;
        }

        stream->id = retval;
        return 0;
}

static int sst_platform_compr_trigger(struct snd_soc_component *component,
                                      struct snd_compr_stream *cstream, int cmd)
{
        struct sst_runtime_stream *stream = cstream->runtime->private_data;

        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                if (stream->compr_ops->stream_start)
                        return stream->compr_ops->stream_start(sst->dev, stream->id);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                if (stream->compr_ops->stream_drop)
                        return stream->compr_ops->stream_drop(sst->dev, stream->id);
                break;
        case SND_COMPR_TRIGGER_DRAIN:
                if (stream->compr_ops->stream_drain)
                        return stream->compr_ops->stream_drain(sst->dev, stream->id);
                break;
        case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
                if (stream->compr_ops->stream_partial_drain)
                        return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (stream->compr_ops->stream_pause)
                        return stream->compr_ops->stream_pause(sst->dev, stream->id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                if (stream->compr_ops->stream_pause_release)
                        return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
                break;
        }
        return -EINVAL;
}

static int sst_platform_compr_pointer(struct snd_soc_component *component,
                                      struct snd_compr_stream *cstream,
                                      struct snd_compr_tstamp64 *tstamp)
{
        struct sst_runtime_stream *stream;
        u64 temp_copied_total = tstamp->copied_total;

        stream = cstream->runtime->private_data;
        stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
        tstamp->byte_offset =
                do_div(temp_copied_total, cstream->runtime->buffer_size);
        pr_debug("calc bytes offset/copied bytes as %u\n", tstamp->byte_offset);
        return 0;
}

static int sst_platform_compr_ack(struct snd_soc_component *component,
                                  struct snd_compr_stream *cstream,
                                  size_t bytes)
{
        struct sst_runtime_stream *stream;

        stream  = cstream->runtime->private_data;
        stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
        stream->bytes_written += bytes;

        return 0;
}

static int sst_platform_compr_get_caps(struct snd_soc_component *component,
                                       struct snd_compr_stream *cstream,
                                       struct snd_compr_caps *caps)
{
        struct sst_runtime_stream *stream =
                cstream->runtime->private_data;

        return stream->compr_ops->get_caps(caps);
}

static int sst_platform_compr_get_codec_caps(struct snd_soc_component *component,
                                             struct snd_compr_stream *cstream,
                                             struct snd_compr_codec_caps *codec)
{
        struct sst_runtime_stream *stream =
                cstream->runtime->private_data;

        return stream->compr_ops->get_codec_caps(codec);
}

static int sst_platform_compr_set_metadata(struct snd_soc_component *component,
                                           struct snd_compr_stream *cstream,
                                           struct snd_compr_metadata *metadata)
{
        struct sst_runtime_stream *stream  =
                 cstream->runtime->private_data;

        return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
}

const struct snd_compress_ops sst_platform_compress_ops = {

        .open = sst_platform_compr_open,
        .free = sst_platform_compr_free,
        .set_params = sst_platform_compr_set_params,
        .set_metadata = sst_platform_compr_set_metadata,
        .trigger = sst_platform_compr_trigger,
        .pointer = sst_platform_compr_pointer,
        .ack = sst_platform_compr_ack,
        .get_caps = sst_platform_compr_get_caps,
        .get_codec_caps = sst_platform_compr_get_codec_caps,
};