162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2021, Linaro Limited 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/init.h> 562306a36Sopenharmony_ci#include <linux/err.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <sound/soc.h> 1062306a36Sopenharmony_ci#include <sound/soc-dapm.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <sound/pcm.h> 1362306a36Sopenharmony_ci#include <asm/dma.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/of_device.h> 1662306a36Sopenharmony_ci#include <sound/pcm_params.h> 1762306a36Sopenharmony_ci#include "q6apm.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DRV_NAME "q6apm-dai" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PLAYBACK_MIN_NUM_PERIODS 2 2262306a36Sopenharmony_ci#define PLAYBACK_MAX_NUM_PERIODS 8 2362306a36Sopenharmony_ci#define PLAYBACK_MAX_PERIOD_SIZE 65536 2462306a36Sopenharmony_ci#define PLAYBACK_MIN_PERIOD_SIZE 128 2562306a36Sopenharmony_ci#define CAPTURE_MIN_NUM_PERIODS 2 2662306a36Sopenharmony_ci#define CAPTURE_MAX_NUM_PERIODS 8 2762306a36Sopenharmony_ci#define CAPTURE_MAX_PERIOD_SIZE 4096 2862306a36Sopenharmony_ci#define CAPTURE_MIN_PERIOD_SIZE 320 2962306a36Sopenharmony_ci#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE) 3062306a36Sopenharmony_ci#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) 3162306a36Sopenharmony_ci#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) 3262306a36Sopenharmony_ci#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) 3362306a36Sopenharmony_ci#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) 3462306a36Sopenharmony_ci#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) 3562306a36Sopenharmony_ci#define SID_MASK_DEFAULT 0xF 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct snd_compr_codec_caps q6apm_compr_caps = { 3862306a36Sopenharmony_ci .num_descriptors = 1, 3962306a36Sopenharmony_ci .descriptor[0].max_ch = 2, 4062306a36Sopenharmony_ci .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, 4162306a36Sopenharmony_ci 24000, 32000, 44100, 48000, 88200, 4262306a36Sopenharmony_ci 96000, 176400, 192000 }, 4362306a36Sopenharmony_ci .descriptor[0].num_sample_rates = 13, 4462306a36Sopenharmony_ci .descriptor[0].bit_rate[0] = 320, 4562306a36Sopenharmony_ci .descriptor[0].bit_rate[1] = 128, 4662306a36Sopenharmony_ci .descriptor[0].num_bitrates = 2, 4762306a36Sopenharmony_ci .descriptor[0].profiles = 0, 4862306a36Sopenharmony_ci .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, 4962306a36Sopenharmony_ci .descriptor[0].formats = 0, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cienum stream_state { 5362306a36Sopenharmony_ci Q6APM_STREAM_IDLE = 0, 5462306a36Sopenharmony_ci Q6APM_STREAM_STOPPED, 5562306a36Sopenharmony_ci Q6APM_STREAM_RUNNING, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct q6apm_dai_rtd { 5962306a36Sopenharmony_ci struct snd_pcm_substream *substream; 6062306a36Sopenharmony_ci struct snd_compr_stream *cstream; 6162306a36Sopenharmony_ci struct snd_codec codec; 6262306a36Sopenharmony_ci struct snd_compr_params codec_param; 6362306a36Sopenharmony_ci struct snd_dma_buffer dma_buffer; 6462306a36Sopenharmony_ci phys_addr_t phys; 6562306a36Sopenharmony_ci unsigned int pcm_size; 6662306a36Sopenharmony_ci unsigned int pcm_count; 6762306a36Sopenharmony_ci unsigned int pos; /* Buffer position */ 6862306a36Sopenharmony_ci unsigned int periods; 6962306a36Sopenharmony_ci unsigned int bytes_sent; 7062306a36Sopenharmony_ci unsigned int bytes_received; 7162306a36Sopenharmony_ci unsigned int copied_total; 7262306a36Sopenharmony_ci uint16_t bits_per_sample; 7362306a36Sopenharmony_ci uint16_t source; /* Encoding source bit mask */ 7462306a36Sopenharmony_ci uint16_t session_id; 7562306a36Sopenharmony_ci bool next_track; 7662306a36Sopenharmony_ci enum stream_state state; 7762306a36Sopenharmony_ci struct q6apm_graph *graph; 7862306a36Sopenharmony_ci spinlock_t lock; 7962306a36Sopenharmony_ci uint32_t initial_samples_drop; 8062306a36Sopenharmony_ci uint32_t trailing_samples_drop; 8162306a36Sopenharmony_ci bool notify_on_drain; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct q6apm_dai_data { 8562306a36Sopenharmony_ci long long sid; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct snd_pcm_hardware q6apm_dai_hardware_capture = { 8962306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | 9062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | 9162306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | 9262306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH), 9362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE), 9462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 9562306a36Sopenharmony_ci .rate_min = 8000, 9662306a36Sopenharmony_ci .rate_max = 48000, 9762306a36Sopenharmony_ci .channels_min = 2, 9862306a36Sopenharmony_ci .channels_max = 4, 9962306a36Sopenharmony_ci .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, 10062306a36Sopenharmony_ci .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 10162306a36Sopenharmony_ci .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 10262306a36Sopenharmony_ci .periods_min = CAPTURE_MIN_NUM_PERIODS, 10362306a36Sopenharmony_ci .periods_max = CAPTURE_MAX_NUM_PERIODS, 10462306a36Sopenharmony_ci .fifo_size = 0, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct snd_pcm_hardware q6apm_dai_hardware_playback = { 10862306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | 10962306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | 11062306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | 11162306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH), 11262306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE), 11362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 11462306a36Sopenharmony_ci .rate_min = 8000, 11562306a36Sopenharmony_ci .rate_max = 192000, 11662306a36Sopenharmony_ci .channels_min = 2, 11762306a36Sopenharmony_ci .channels_max = 8, 11862306a36Sopenharmony_ci .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE), 11962306a36Sopenharmony_ci .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 12062306a36Sopenharmony_ci .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 12162306a36Sopenharmony_ci .periods_min = PLAYBACK_MIN_NUM_PERIODS, 12262306a36Sopenharmony_ci .periods_max = PLAYBACK_MAX_NUM_PERIODS, 12362306a36Sopenharmony_ci .fifo_size = 0, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = priv; 12962306a36Sopenharmony_ci struct snd_pcm_substream *substream = prtd->substream; 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci switch (opcode) { 13362306a36Sopenharmony_ci case APM_CLIENT_EVENT_CMD_EOS_DONE: 13462306a36Sopenharmony_ci prtd->state = Q6APM_STREAM_STOPPED; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case APM_CLIENT_EVENT_DATA_WRITE_DONE: 13762306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 13862306a36Sopenharmony_ci prtd->pos += prtd->pcm_count; 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 14062306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 14162306a36Sopenharmony_ci if (prtd->state == Q6APM_STREAM_RUNNING) 14262306a36Sopenharmony_ci q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case APM_CLIENT_EVENT_DATA_READ_DONE: 14662306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 14762306a36Sopenharmony_ci prtd->pos += prtd->pcm_count; 14862306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 14962306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 15062306a36Sopenharmony_ci if (prtd->state == Q6APM_STREAM_RUNNING) 15162306a36Sopenharmony_ci q6apm_read(prtd->graph); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void event_handler_compr(uint32_t opcode, uint32_t token, 16062306a36Sopenharmony_ci uint32_t *payload, void *priv) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = priv; 16362306a36Sopenharmony_ci struct snd_compr_stream *substream = prtd->cstream; 16462306a36Sopenharmony_ci unsigned long flags; 16562306a36Sopenharmony_ci uint32_t wflags = 0; 16662306a36Sopenharmony_ci uint64_t avail; 16762306a36Sopenharmony_ci uint32_t bytes_written, bytes_to_write; 16862306a36Sopenharmony_ci bool is_last_buffer = false; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci switch (opcode) { 17162306a36Sopenharmony_ci case APM_CLIENT_EVENT_CMD_EOS_DONE: 17262306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 17362306a36Sopenharmony_ci if (prtd->notify_on_drain) { 17462306a36Sopenharmony_ci snd_compr_drain_notify(prtd->cstream); 17562306a36Sopenharmony_ci prtd->notify_on_drain = false; 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci prtd->state = Q6APM_STREAM_STOPPED; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case APM_CLIENT_EVENT_DATA_WRITE_DONE: 18262306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 18362306a36Sopenharmony_ci bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT; 18462306a36Sopenharmony_ci prtd->copied_total += bytes_written; 18562306a36Sopenharmony_ci snd_compr_fragment_elapsed(substream); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (prtd->state != Q6APM_STREAM_RUNNING) { 18862306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (avail > prtd->pcm_count) { 19562306a36Sopenharmony_ci bytes_to_write = prtd->pcm_count; 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci if (substream->partial_drain || prtd->notify_on_drain) 19862306a36Sopenharmony_ci is_last_buffer = true; 19962306a36Sopenharmony_ci bytes_to_write = avail; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (bytes_to_write) { 20362306a36Sopenharmony_ci if (substream->partial_drain && is_last_buffer) 20462306a36Sopenharmony_ci wflags |= APM_LAST_BUFFER_FLAG; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci q6apm_write_async(prtd->graph, 20762306a36Sopenharmony_ci bytes_to_write, 0, 0, wflags); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (prtd->notify_on_drain && is_last_buffer) 21262306a36Sopenharmony_ci audioreach_shared_memory_send_eos(prtd->graph); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int q6apm_dai_prepare(struct snd_soc_component *component, 22362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 22662306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 22762306a36Sopenharmony_ci struct audioreach_module_config cfg; 22862306a36Sopenharmony_ci struct device *dev = component->dev; 22962306a36Sopenharmony_ci struct q6apm_dai_data *pdata; 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 23362306a36Sopenharmony_ci if (!pdata) 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (!prtd || !prtd->graph) { 23762306a36Sopenharmony_ci dev_err(dev, "%s: private data null or audio client freed\n", __func__); 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci cfg.direction = substream->stream; 24262306a36Sopenharmony_ci cfg.sample_rate = runtime->rate; 24362306a36Sopenharmony_ci cfg.num_channels = runtime->channels; 24462306a36Sopenharmony_ci cfg.bit_width = prtd->bits_per_sample; 24562306a36Sopenharmony_ci cfg.fmt = SND_AUDIOCODEC_PCM; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (prtd->state) { 24862306a36Sopenharmony_ci /* clear the previous setup if any */ 24962306a36Sopenharmony_ci q6apm_graph_stop(prtd->graph); 25062306a36Sopenharmony_ci q6apm_unmap_memory_regions(prtd->graph, substream->stream); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci prtd->pcm_count = snd_pcm_lib_period_bytes(substream); 25462306a36Sopenharmony_ci prtd->pos = 0; 25562306a36Sopenharmony_ci /* rate and channels are sent to audio driver */ 25662306a36Sopenharmony_ci ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); 25762306a36Sopenharmony_ci if (ret < 0) { 25862306a36Sopenharmony_ci dev_err(dev, "%s: q6apm_open_write failed\n", __func__); 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); 26362306a36Sopenharmony_ci if (ret < 0) 26462306a36Sopenharmony_ci dev_err(dev, "%s: CMD Format block failed\n", __func__); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys, 26762306a36Sopenharmony_ci (prtd->pcm_size / prtd->periods), prtd->periods); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (ret < 0) { 27062306a36Sopenharmony_ci dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); 27162306a36Sopenharmony_ci return -ENOMEM; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = q6apm_graph_prepare(prtd->graph); 27562306a36Sopenharmony_ci if (ret) { 27662306a36Sopenharmony_ci dev_err(dev, "Failed to prepare Graph %d\n", ret); 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = q6apm_graph_start(prtd->graph); 28162306a36Sopenharmony_ci if (ret) { 28262306a36Sopenharmony_ci dev_err(dev, "Failed to Start Graph %d\n", ret); 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 28762306a36Sopenharmony_ci int i; 28862306a36Sopenharmony_ci /* Queue the buffers for Capture ONLY after graph is started */ 28962306a36Sopenharmony_ci for (i = 0; i < runtime->periods; i++) 29062306a36Sopenharmony_ci q6apm_read(prtd->graph); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Now that graph as been prepared and started update the internal state accordingly */ 29562306a36Sopenharmony_ci prtd->state = Q6APM_STREAM_RUNNING; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int q6apm_dai_trigger(struct snd_soc_component *component, 30162306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 30462306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 30562306a36Sopenharmony_ci int ret = 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (cmd) { 30862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 30962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 31062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 31162306a36Sopenharmony_ci /* start writing buffers for playback only as we already queued capture buffers */ 31262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 31362306a36Sopenharmony_ci ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0); 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 31662306a36Sopenharmony_ci /* TODO support be handled via SoftPause Module */ 31762306a36Sopenharmony_ci prtd->state = Q6APM_STREAM_STOPPED; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 32062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci ret = -EINVAL; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int q6apm_dai_open(struct snd_soc_component *component, 33162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 33462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; 33562306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); 33662306a36Sopenharmony_ci struct device *dev = component->dev; 33762306a36Sopenharmony_ci struct q6apm_dai_data *pdata; 33862306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd; 33962306a36Sopenharmony_ci int graph_id, ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci graph_id = cpu_dai->driver->id; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 34462306a36Sopenharmony_ci if (!pdata) { 34562306a36Sopenharmony_ci dev_err(dev, "Drv data not found ..\n"); 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 35062306a36Sopenharmony_ci if (prtd == NULL) 35162306a36Sopenharmony_ci return -ENOMEM; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci spin_lock_init(&prtd->lock); 35462306a36Sopenharmony_ci prtd->substream = substream; 35562306a36Sopenharmony_ci prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id); 35662306a36Sopenharmony_ci if (IS_ERR(prtd->graph)) { 35762306a36Sopenharmony_ci dev_err(dev, "%s: Could not allocate memory\n", __func__); 35862306a36Sopenharmony_ci ret = PTR_ERR(prtd->graph); 35962306a36Sopenharmony_ci goto err; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 36362306a36Sopenharmony_ci runtime->hw = q6apm_dai_hardware_playback; 36462306a36Sopenharmony_ci else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 36562306a36Sopenharmony_ci runtime->hw = q6apm_dai_hardware_capture; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Ensure that buffer size is a multiple of period size */ 36862306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 36962306a36Sopenharmony_ci if (ret < 0) { 37062306a36Sopenharmony_ci dev_err(dev, "snd_pcm_hw_constraint_integer failed\n"); 37162306a36Sopenharmony_ci goto err; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 37562306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 37662306a36Sopenharmony_ci BUFFER_BYTES_MIN, BUFFER_BYTES_MAX); 37762306a36Sopenharmony_ci if (ret < 0) { 37862306a36Sopenharmony_ci dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret); 37962306a36Sopenharmony_ci goto err; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 38462306a36Sopenharmony_ci if (ret < 0) { 38562306a36Sopenharmony_ci dev_err(dev, "constraint for period bytes step ret = %d\n", ret); 38662306a36Sopenharmony_ci goto err; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 39062306a36Sopenharmony_ci if (ret < 0) { 39162306a36Sopenharmony_ci dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret); 39262306a36Sopenharmony_ci goto err; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci runtime->private_data = prtd; 39662306a36Sopenharmony_ci runtime->dma_bytes = BUFFER_BYTES_MAX; 39762306a36Sopenharmony_ci if (pdata->sid < 0) 39862306a36Sopenharmony_ci prtd->phys = substream->dma_buffer.addr; 39962306a36Sopenharmony_ci else 40062306a36Sopenharmony_ci prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_cierr: 40462306a36Sopenharmony_ci kfree(prtd); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return ret; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int q6apm_dai_close(struct snd_soc_component *component, 41062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 41362306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (prtd->state) { /* only stop graph that is started */ 41662306a36Sopenharmony_ci q6apm_graph_stop(prtd->graph); 41762306a36Sopenharmony_ci q6apm_unmap_memory_regions(prtd->graph, substream->stream); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci q6apm_graph_close(prtd->graph); 42162306a36Sopenharmony_ci prtd->graph = NULL; 42262306a36Sopenharmony_ci kfree(prtd); 42362306a36Sopenharmony_ci runtime->private_data = NULL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, 42962306a36Sopenharmony_ci struct snd_pcm_substream *substream) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 43262306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 43362306a36Sopenharmony_ci snd_pcm_uframes_t ptr; 43462306a36Sopenharmony_ci unsigned long flags; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 43762306a36Sopenharmony_ci if (prtd->pos == prtd->pcm_size) 43862306a36Sopenharmony_ci prtd->pos = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ptr = bytes_to_frames(runtime, prtd->pos); 44162306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return ptr; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int q6apm_dai_hw_params(struct snd_soc_component *component, 44762306a36Sopenharmony_ci struct snd_pcm_substream *substream, 44862306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 45162306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci prtd->pcm_size = params_buffer_bytes(params); 45462306a36Sopenharmony_ci prtd->periods = params_periods(params); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci switch (params_format(params)) { 45762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 45862306a36Sopenharmony_ci prtd->bits_per_sample = 16; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 46162306a36Sopenharmony_ci prtd->bits_per_sample = 24; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci default: 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci int size = BUFFER_BYTES_MAX; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int q6apm_dai_compr_open(struct snd_soc_component *component, 47862306a36Sopenharmony_ci struct snd_compr_stream *stream) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 48162306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 48262306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 48362306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd; 48462306a36Sopenharmony_ci struct q6apm_dai_data *pdata; 48562306a36Sopenharmony_ci struct device *dev = component->dev; 48662306a36Sopenharmony_ci int ret, size; 48762306a36Sopenharmony_ci int graph_id; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci graph_id = cpu_dai->driver->id; 49062306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 49162306a36Sopenharmony_ci if (!pdata) 49262306a36Sopenharmony_ci return -EINVAL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 49562306a36Sopenharmony_ci if (prtd == NULL) 49662306a36Sopenharmony_ci return -ENOMEM; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci prtd->cstream = stream; 49962306a36Sopenharmony_ci prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id); 50062306a36Sopenharmony_ci if (IS_ERR(prtd->graph)) { 50162306a36Sopenharmony_ci ret = PTR_ERR(prtd->graph); 50262306a36Sopenharmony_ci kfree(prtd); 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci runtime->private_data = prtd; 50762306a36Sopenharmony_ci runtime->dma_bytes = BUFFER_BYTES_MAX; 50862306a36Sopenharmony_ci size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 50962306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (pdata->sid < 0) 51462306a36Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr; 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); 51962306a36Sopenharmony_ci spin_lock_init(&prtd->lock); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci q6apm_enable_compress_module(dev, prtd->graph, true); 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int q6apm_dai_compr_free(struct snd_soc_component *component, 52662306a36Sopenharmony_ci struct snd_compr_stream *stream) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 52962306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci q6apm_graph_stop(prtd->graph); 53262306a36Sopenharmony_ci q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); 53362306a36Sopenharmony_ci q6apm_graph_close(prtd->graph); 53462306a36Sopenharmony_ci snd_dma_free_pages(&prtd->dma_buffer); 53562306a36Sopenharmony_ci prtd->graph = NULL; 53662306a36Sopenharmony_ci kfree(prtd); 53762306a36Sopenharmony_ci runtime->private_data = NULL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int q6apm_dai_compr_get_caps(struct snd_soc_component *component, 54362306a36Sopenharmony_ci struct snd_compr_stream *stream, 54462306a36Sopenharmony_ci struct snd_compr_caps *caps) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci caps->direction = SND_COMPRESS_PLAYBACK; 54762306a36Sopenharmony_ci caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; 54862306a36Sopenharmony_ci caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; 54962306a36Sopenharmony_ci caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; 55062306a36Sopenharmony_ci caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 55162306a36Sopenharmony_ci caps->num_codecs = 3; 55262306a36Sopenharmony_ci caps->codecs[0] = SND_AUDIOCODEC_MP3; 55362306a36Sopenharmony_ci caps->codecs[1] = SND_AUDIOCODEC_AAC; 55462306a36Sopenharmony_ci caps->codecs[2] = SND_AUDIOCODEC_FLAC; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component, 56062306a36Sopenharmony_ci struct snd_compr_stream *stream, 56162306a36Sopenharmony_ci struct snd_compr_codec_caps *codec) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci switch (codec->codec) { 56462306a36Sopenharmony_ci case SND_AUDIOCODEC_MP3: 56562306a36Sopenharmony_ci *codec = q6apm_compr_caps; 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci default: 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int q6apm_dai_compr_pointer(struct snd_soc_component *component, 57562306a36Sopenharmony_ci struct snd_compr_stream *stream, 57662306a36Sopenharmony_ci struct snd_compr_tstamp *tstamp) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 57962306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 58062306a36Sopenharmony_ci unsigned long flags; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 58362306a36Sopenharmony_ci tstamp->copied_total = prtd->copied_total; 58462306a36Sopenharmony_ci tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; 58562306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int q6apm_dai_compr_trigger(struct snd_soc_component *component, 59162306a36Sopenharmony_ci struct snd_compr_stream *stream, int cmd) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 59462306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 59562306a36Sopenharmony_ci int ret = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (cmd) { 59862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 59962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 60062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 60162306a36Sopenharmony_ci ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP); 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 60662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case SND_COMPR_TRIGGER_NEXT_TRACK: 60962306a36Sopenharmony_ci prtd->next_track = true; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case SND_COMPR_TRIGGER_DRAIN: 61262306a36Sopenharmony_ci case SND_COMPR_TRIGGER_PARTIAL_DRAIN: 61362306a36Sopenharmony_ci prtd->notify_on_drain = true; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci ret = -EINVAL; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream, 62462306a36Sopenharmony_ci size_t count) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 62762306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 62862306a36Sopenharmony_ci unsigned long flags; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 63162306a36Sopenharmony_ci prtd->bytes_received += count; 63262306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return count; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int q6apm_dai_compr_set_params(struct snd_soc_component *component, 63862306a36Sopenharmony_ci struct snd_compr_stream *stream, 63962306a36Sopenharmony_ci struct snd_compr_params *params) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 64262306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 64362306a36Sopenharmony_ci struct q6apm_dai_data *pdata; 64462306a36Sopenharmony_ci struct audioreach_module_config cfg; 64562306a36Sopenharmony_ci struct snd_codec *codec = ¶ms->codec; 64662306a36Sopenharmony_ci int dir = stream->direction; 64762306a36Sopenharmony_ci int ret; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 65062306a36Sopenharmony_ci if (!pdata) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci prtd->periods = runtime->fragments; 65462306a36Sopenharmony_ci prtd->pcm_count = runtime->fragment_size; 65562306a36Sopenharmony_ci prtd->pcm_size = runtime->fragments * runtime->fragment_size; 65662306a36Sopenharmony_ci prtd->bits_per_sample = 16; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci prtd->pos = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (prtd->next_track != true) { 66162306a36Sopenharmony_ci memcpy(&prtd->codec, codec, sizeof(*codec)); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id); 66462306a36Sopenharmony_ci if (ret) 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci cfg.direction = dir; 66862306a36Sopenharmony_ci cfg.sample_rate = codec->sample_rate; 66962306a36Sopenharmony_ci cfg.num_channels = 2; 67062306a36Sopenharmony_ci cfg.bit_width = prtd->bits_per_sample; 67162306a36Sopenharmony_ci cfg.fmt = codec->id; 67262306a36Sopenharmony_ci memcpy(&cfg.codec, codec, sizeof(*codec)); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); 67562306a36Sopenharmony_ci if (ret < 0) 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, 68362306a36Sopenharmony_ci prtd->phys, (prtd->pcm_size / prtd->periods), 68462306a36Sopenharmony_ci prtd->periods); 68562306a36Sopenharmony_ci if (ret < 0) 68662306a36Sopenharmony_ci return -ENOMEM; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ret = q6apm_graph_prepare(prtd->graph); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci ret = q6apm_graph_start(prtd->graph); 69362306a36Sopenharmony_ci if (ret) 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci cfg.direction = dir; 69862306a36Sopenharmony_ci cfg.sample_rate = codec->sample_rate; 69962306a36Sopenharmony_ci cfg.num_channels = 2; 70062306a36Sopenharmony_ci cfg.bit_width = prtd->bits_per_sample; 70162306a36Sopenharmony_ci cfg.fmt = codec->id; 70262306a36Sopenharmony_ci memcpy(&cfg.codec, codec, sizeof(*codec)); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ret = audioreach_compr_set_param(prtd->graph, &cfg); 70562306a36Sopenharmony_ci if (ret < 0) 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci prtd->state = Q6APM_STREAM_RUNNING; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int q6apm_dai_compr_set_metadata(struct snd_soc_component *component, 71462306a36Sopenharmony_ci struct snd_compr_stream *stream, 71562306a36Sopenharmony_ci struct snd_compr_metadata *metadata) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 71862306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 71962306a36Sopenharmony_ci int ret = 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci switch (metadata->key) { 72262306a36Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_PADDING: 72362306a36Sopenharmony_ci prtd->trailing_samples_drop = metadata->value[0]; 72462306a36Sopenharmony_ci q6apm_remove_trailing_silence(component->dev, prtd->graph, 72562306a36Sopenharmony_ci prtd->trailing_samples_drop); 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_DELAY: 72862306a36Sopenharmony_ci prtd->initial_samples_drop = metadata->value[0]; 72962306a36Sopenharmony_ci q6apm_remove_initial_silence(component->dev, prtd->graph, 73062306a36Sopenharmony_ci prtd->initial_samples_drop); 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci default: 73362306a36Sopenharmony_ci ret = -EINVAL; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int q6apm_dai_compr_mmap(struct snd_soc_component *component, 74162306a36Sopenharmony_ci struct snd_compr_stream *stream, 74262306a36Sopenharmony_ci struct vm_area_struct *vma) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 74562306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 74662306a36Sopenharmony_ci struct device *dev = component->dev; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr, 74962306a36Sopenharmony_ci prtd->dma_buffer.bytes); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic int q6apm_compr_copy(struct snd_soc_component *component, 75362306a36Sopenharmony_ci struct snd_compr_stream *stream, char __user *buf, 75462306a36Sopenharmony_ci size_t count) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 75762306a36Sopenharmony_ci struct q6apm_dai_rtd *prtd = runtime->private_data; 75862306a36Sopenharmony_ci void *dstn; 75962306a36Sopenharmony_ci unsigned long flags; 76062306a36Sopenharmony_ci size_t copy; 76162306a36Sopenharmony_ci u32 wflags = 0; 76262306a36Sopenharmony_ci u32 app_pointer; 76362306a36Sopenharmony_ci u32 bytes_received; 76462306a36Sopenharmony_ci uint32_t bytes_to_write; 76562306a36Sopenharmony_ci int avail, bytes_in_flight = 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci bytes_received = prtd->bytes_received; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /** 77062306a36Sopenharmony_ci * Make sure that next track data pointer is aligned at 32 bit boundary 77162306a36Sopenharmony_ci * This is a Mandatory requirement from DSP data buffers alignment 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci if (prtd->next_track) 77462306a36Sopenharmony_ci bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci app_pointer = bytes_received/prtd->pcm_size; 77762306a36Sopenharmony_ci app_pointer = bytes_received - (app_pointer * prtd->pcm_size); 77862306a36Sopenharmony_ci dstn = prtd->dma_buffer.area + app_pointer; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (count < prtd->pcm_size - app_pointer) { 78162306a36Sopenharmony_ci if (copy_from_user(dstn, buf, count)) 78262306a36Sopenharmony_ci return -EFAULT; 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci copy = prtd->pcm_size - app_pointer; 78562306a36Sopenharmony_ci if (copy_from_user(dstn, buf, copy)) 78662306a36Sopenharmony_ci return -EFAULT; 78762306a36Sopenharmony_ci if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy)) 78862306a36Sopenharmony_ci return -EFAULT; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 79262306a36Sopenharmony_ci bytes_in_flight = prtd->bytes_received - prtd->copied_total; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (prtd->next_track) { 79562306a36Sopenharmony_ci prtd->next_track = false; 79662306a36Sopenharmony_ci prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); 79762306a36Sopenharmony_ci prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci prtd->bytes_received = bytes_received + count; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Kick off the data to dsp if its starving!! */ 80362306a36Sopenharmony_ci if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) { 80462306a36Sopenharmony_ci bytes_to_write = prtd->pcm_count; 80562306a36Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (avail < prtd->pcm_count) 80862306a36Sopenharmony_ci bytes_to_write = avail; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags); 81162306a36Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return count; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic const struct snd_compress_ops q6apm_dai_compress_ops = { 82062306a36Sopenharmony_ci .open = q6apm_dai_compr_open, 82162306a36Sopenharmony_ci .free = q6apm_dai_compr_free, 82262306a36Sopenharmony_ci .get_caps = q6apm_dai_compr_get_caps, 82362306a36Sopenharmony_ci .get_codec_caps = q6apm_dai_compr_get_codec_caps, 82462306a36Sopenharmony_ci .pointer = q6apm_dai_compr_pointer, 82562306a36Sopenharmony_ci .trigger = q6apm_dai_compr_trigger, 82662306a36Sopenharmony_ci .ack = q6apm_dai_compr_ack, 82762306a36Sopenharmony_ci .set_params = q6apm_dai_compr_set_params, 82862306a36Sopenharmony_ci .set_metadata = q6apm_dai_compr_set_metadata, 82962306a36Sopenharmony_ci .mmap = q6apm_dai_compr_mmap, 83062306a36Sopenharmony_ci .copy = q6apm_compr_copy, 83162306a36Sopenharmony_ci}; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic const struct snd_soc_component_driver q6apm_fe_dai_component = { 83462306a36Sopenharmony_ci .name = DRV_NAME, 83562306a36Sopenharmony_ci .open = q6apm_dai_open, 83662306a36Sopenharmony_ci .close = q6apm_dai_close, 83762306a36Sopenharmony_ci .prepare = q6apm_dai_prepare, 83862306a36Sopenharmony_ci .pcm_construct = q6apm_dai_pcm_new, 83962306a36Sopenharmony_ci .hw_params = q6apm_dai_hw_params, 84062306a36Sopenharmony_ci .pointer = q6apm_dai_pointer, 84162306a36Sopenharmony_ci .trigger = q6apm_dai_trigger, 84262306a36Sopenharmony_ci .compress_ops = &q6apm_dai_compress_ops, 84362306a36Sopenharmony_ci .use_dai_pcm_id = true, 84462306a36Sopenharmony_ci}; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic int q6apm_dai_probe(struct platform_device *pdev) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 84962306a36Sopenharmony_ci struct device_node *node = dev->of_node; 85062306a36Sopenharmony_ci struct q6apm_dai_data *pdata; 85162306a36Sopenharmony_ci struct of_phandle_args args; 85262306a36Sopenharmony_ci int rc; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 85562306a36Sopenharmony_ci if (!pdata) 85662306a36Sopenharmony_ci return -ENOMEM; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); 85962306a36Sopenharmony_ci if (rc < 0) 86062306a36Sopenharmony_ci pdata->sid = -1; 86162306a36Sopenharmony_ci else 86262306a36Sopenharmony_ci pdata->sid = args.args[0] & SID_MASK_DEFAULT; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci dev_set_drvdata(dev, pdata); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci#ifdef CONFIG_OF 87062306a36Sopenharmony_cistatic const struct of_device_id q6apm_dai_device_id[] = { 87162306a36Sopenharmony_ci { .compatible = "qcom,q6apm-dais" }, 87262306a36Sopenharmony_ci {}, 87362306a36Sopenharmony_ci}; 87462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6apm_dai_device_id); 87562306a36Sopenharmony_ci#endif 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic struct platform_driver q6apm_dai_platform_driver = { 87862306a36Sopenharmony_ci .driver = { 87962306a36Sopenharmony_ci .name = "q6apm-dai", 88062306a36Sopenharmony_ci .of_match_table = of_match_ptr(q6apm_dai_device_id), 88162306a36Sopenharmony_ci }, 88262306a36Sopenharmony_ci .probe = q6apm_dai_probe, 88362306a36Sopenharmony_ci}; 88462306a36Sopenharmony_cimodule_platform_driver(q6apm_dai_platform_driver); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ciMODULE_DESCRIPTION("Q6APM dai driver"); 88762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 888