18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  sst_mfld_platform.c - Intel MID Platform driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2010-2014 Intel Corp
68c2ecf20Sopenharmony_ci *  Author: Vinod Koul <vinod.koul@intel.com>
78c2ecf20Sopenharmony_ci *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <sound/core.h>
178c2ecf20Sopenharmony_ci#include <sound/pcm.h>
188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
198c2ecf20Sopenharmony_ci#include <sound/soc.h>
208c2ecf20Sopenharmony_ci#include <sound/compress_driver.h>
218c2ecf20Sopenharmony_ci#include "sst-mfld-platform.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* compress stream operations */
248c2ecf20Sopenharmony_cistatic void sst_compr_fragment_elapsed(void *arg)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	pr_debug("fragment elapsed by driver\n");
298c2ecf20Sopenharmony_ci	if (cstream)
308c2ecf20Sopenharmony_ci		snd_compr_fragment_elapsed(cstream);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void sst_drain_notify(void *arg)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	pr_debug("drain notify by driver\n");
388c2ecf20Sopenharmony_ci	if (cstream)
398c2ecf20Sopenharmony_ci		snd_compr_drain_notify(cstream);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int sst_platform_compr_open(struct snd_soc_component *component,
438c2ecf20Sopenharmony_ci				   struct snd_compr_stream *cstream)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	int ret_val;
468c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = cstream->runtime;
478c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
508c2ecf20Sopenharmony_ci	if (!stream)
518c2ecf20Sopenharmony_ci		return -ENOMEM;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	spin_lock_init(&stream->status_lock);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* get the sst ops */
568c2ecf20Sopenharmony_ci	if (!sst || !try_module_get(sst->dev->driver->owner)) {
578c2ecf20Sopenharmony_ci		pr_err("no device available to run\n");
588c2ecf20Sopenharmony_ci		ret_val = -ENODEV;
598c2ecf20Sopenharmony_ci		goto out_ops;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	stream->compr_ops = sst->compr_ops;
628c2ecf20Sopenharmony_ci	stream->id = 0;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* Turn on LPE */
658c2ecf20Sopenharmony_ci	sst->compr_ops->power(sst->dev, true);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	sst_set_stream_status(stream, SST_PLATFORM_INIT);
688c2ecf20Sopenharmony_ci	runtime->private_data = stream;
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ciout_ops:
718c2ecf20Sopenharmony_ci	kfree(stream);
728c2ecf20Sopenharmony_ci	return ret_val;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int sst_platform_compr_free(struct snd_soc_component *component,
768c2ecf20Sopenharmony_ci				   struct snd_compr_stream *cstream)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream;
798c2ecf20Sopenharmony_ci	int ret_val = 0, str_id;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	stream = cstream->runtime->private_data;
828c2ecf20Sopenharmony_ci	/* Turn off LPE */
838c2ecf20Sopenharmony_ci	sst->compr_ops->power(sst->dev, false);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*need to check*/
868c2ecf20Sopenharmony_ci	str_id = stream->id;
878c2ecf20Sopenharmony_ci	if (str_id)
888c2ecf20Sopenharmony_ci		ret_val = stream->compr_ops->close(sst->dev, str_id);
898c2ecf20Sopenharmony_ci	module_put(sst->dev->driver->owner);
908c2ecf20Sopenharmony_ci	kfree(stream);
918c2ecf20Sopenharmony_ci	pr_debug("%s: %d\n", __func__, ret_val);
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int sst_platform_compr_set_params(struct snd_soc_component *component,
968c2ecf20Sopenharmony_ci					 struct snd_compr_stream *cstream,
978c2ecf20Sopenharmony_ci					 struct snd_compr_params *params)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream;
1008c2ecf20Sopenharmony_ci	int retval;
1018c2ecf20Sopenharmony_ci	struct snd_sst_params str_params;
1028c2ecf20Sopenharmony_ci	struct sst_compress_cb cb;
1038c2ecf20Sopenharmony_ci	struct sst_data *ctx = snd_soc_component_get_drvdata(component);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	stream = cstream->runtime->private_data;
1068c2ecf20Sopenharmony_ci	/* construct fw structure for this*/
1078c2ecf20Sopenharmony_ci	memset(&str_params, 0, sizeof(str_params));
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* fill the device type and stream id to pass to SST driver */
1108c2ecf20Sopenharmony_ci	retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
1118c2ecf20Sopenharmony_ci	pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
1128c2ecf20Sopenharmony_ci	if (retval < 0)
1138c2ecf20Sopenharmony_ci		return retval;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	switch (params->codec.id) {
1168c2ecf20Sopenharmony_ci	case SND_AUDIOCODEC_MP3: {
1178c2ecf20Sopenharmony_ci		str_params.codec = SST_CODEC_TYPE_MP3;
1188c2ecf20Sopenharmony_ci		str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
1198c2ecf20Sopenharmony_ci		str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	case SND_AUDIOCODEC_AAC: {
1248c2ecf20Sopenharmony_ci		str_params.codec = SST_CODEC_TYPE_AAC;
1258c2ecf20Sopenharmony_ci		str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
1268c2ecf20Sopenharmony_ci		str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
1278c2ecf20Sopenharmony_ci		if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
1288c2ecf20Sopenharmony_ci			str_params.sparams.uc.aac_params.bs_format =
1298c2ecf20Sopenharmony_ci							AAC_BIT_STREAM_ADTS;
1308c2ecf20Sopenharmony_ci		else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
1318c2ecf20Sopenharmony_ci			str_params.sparams.uc.aac_params.bs_format =
1328c2ecf20Sopenharmony_ci							AAC_BIT_STREAM_RAW;
1338c2ecf20Sopenharmony_ci		else {
1348c2ecf20Sopenharmony_ci			pr_err("Undefined format%d\n", params->codec.format);
1358c2ecf20Sopenharmony_ci			return -EINVAL;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci		str_params.sparams.uc.aac_params.externalsr =
1388c2ecf20Sopenharmony_ci						params->codec.sample_rate;
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	default:
1438c2ecf20Sopenharmony_ci		pr_err("codec not supported, id =%d\n", params->codec.id);
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	str_params.aparams.ring_buf_info[0].addr  =
1488c2ecf20Sopenharmony_ci					virt_to_phys(cstream->runtime->buffer);
1498c2ecf20Sopenharmony_ci	str_params.aparams.ring_buf_info[0].size =
1508c2ecf20Sopenharmony_ci					cstream->runtime->buffer_size;
1518c2ecf20Sopenharmony_ci	str_params.aparams.sg_count = 1;
1528c2ecf20Sopenharmony_ci	str_params.aparams.frag_size = cstream->runtime->fragment_size;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	cb.param = cstream;
1558c2ecf20Sopenharmony_ci	cb.compr_cb = sst_compr_fragment_elapsed;
1568c2ecf20Sopenharmony_ci	cb.drain_cb_param = cstream;
1578c2ecf20Sopenharmony_ci	cb.drain_notify = sst_drain_notify;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
1608c2ecf20Sopenharmony_ci	if (retval < 0) {
1618c2ecf20Sopenharmony_ci		pr_err("stream allocation failed %d\n", retval);
1628c2ecf20Sopenharmony_ci		return retval;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	stream->id = retval;
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int sst_platform_compr_trigger(struct snd_soc_component *component,
1708c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream, int cmd)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream = cstream->runtime->private_data;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	switch (cmd) {
1758c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
1768c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_start)
1778c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_start(sst->dev, stream->id);
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
1808c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_drop)
1818c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_drop(sst->dev, stream->id);
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case SND_COMPR_TRIGGER_DRAIN:
1848c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_drain)
1858c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_drain(sst->dev, stream->id);
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
1888c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_partial_drain)
1898c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1928c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_pause)
1938c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_pause(sst->dev, stream->id);
1948c2ecf20Sopenharmony_ci		break;
1958c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1968c2ecf20Sopenharmony_ci		if (stream->compr_ops->stream_pause_release)
1978c2ecf20Sopenharmony_ci			return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	return -EINVAL;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int sst_platform_compr_pointer(struct snd_soc_component *component,
2048c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream,
2058c2ecf20Sopenharmony_ci				      struct snd_compr_tstamp *tstamp)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	stream  = cstream->runtime->private_data;
2108c2ecf20Sopenharmony_ci	stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
2118c2ecf20Sopenharmony_ci	tstamp->byte_offset = tstamp->copied_total %
2128c2ecf20Sopenharmony_ci				 (u32)cstream->runtime->buffer_size;
2138c2ecf20Sopenharmony_ci	pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int sst_platform_compr_ack(struct snd_soc_component *component,
2188c2ecf20Sopenharmony_ci				  struct snd_compr_stream *cstream,
2198c2ecf20Sopenharmony_ci				  size_t bytes)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	stream  = cstream->runtime->private_data;
2248c2ecf20Sopenharmony_ci	stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
2258c2ecf20Sopenharmony_ci	stream->bytes_written += bytes;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int sst_platform_compr_get_caps(struct snd_soc_component *component,
2318c2ecf20Sopenharmony_ci				       struct snd_compr_stream *cstream,
2328c2ecf20Sopenharmony_ci				       struct snd_compr_caps *caps)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream =
2358c2ecf20Sopenharmony_ci		cstream->runtime->private_data;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return stream->compr_ops->get_caps(caps);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int sst_platform_compr_get_codec_caps(struct snd_soc_component *component,
2418c2ecf20Sopenharmony_ci					     struct snd_compr_stream *cstream,
2428c2ecf20Sopenharmony_ci					     struct snd_compr_codec_caps *codec)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream =
2458c2ecf20Sopenharmony_ci		cstream->runtime->private_data;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return stream->compr_ops->get_codec_caps(codec);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int sst_platform_compr_set_metadata(struct snd_soc_component *component,
2518c2ecf20Sopenharmony_ci					   struct snd_compr_stream *cstream,
2528c2ecf20Sopenharmony_ci					   struct snd_compr_metadata *metadata)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct sst_runtime_stream *stream  =
2558c2ecf20Sopenharmony_ci		 cstream->runtime->private_data;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ciconst struct snd_compress_ops sst_platform_compress_ops = {
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	.open = sst_platform_compr_open,
2638c2ecf20Sopenharmony_ci	.free = sst_platform_compr_free,
2648c2ecf20Sopenharmony_ci	.set_params = sst_platform_compr_set_params,
2658c2ecf20Sopenharmony_ci	.set_metadata = sst_platform_compr_set_metadata,
2668c2ecf20Sopenharmony_ci	.trigger = sst_platform_compr_trigger,
2678c2ecf20Sopenharmony_ci	.pointer = sst_platform_compr_pointer,
2688c2ecf20Sopenharmony_ci	.ack = sst_platform_compr_ack,
2698c2ecf20Sopenharmony_ci	.get_caps = sst_platform_compr_get_caps,
2708c2ecf20Sopenharmony_ci	.get_codec_caps = sst_platform_compr_get_codec_caps,
2718c2ecf20Sopenharmony_ci};
272