162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/err.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <sound/soc.h> 1162306a36Sopenharmony_ci#include <sound/soc-dapm.h> 1262306a36Sopenharmony_ci#include <sound/pcm.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <sound/compress_driver.h> 1562306a36Sopenharmony_ci#include <asm/dma.h> 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/of_device.h> 1862306a36Sopenharmony_ci#include <sound/pcm_params.h> 1962306a36Sopenharmony_ci#include "q6asm.h" 2062306a36Sopenharmony_ci#include "q6routing.h" 2162306a36Sopenharmony_ci#include "q6dsp-errno.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DRV_NAME "q6asm-fe-dai" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PLAYBACK_MIN_NUM_PERIODS 2 2662306a36Sopenharmony_ci#define PLAYBACK_MAX_NUM_PERIODS 8 2762306a36Sopenharmony_ci#define PLAYBACK_MAX_PERIOD_SIZE 65536 2862306a36Sopenharmony_ci#define PLAYBACK_MIN_PERIOD_SIZE 128 2962306a36Sopenharmony_ci#define CAPTURE_MIN_NUM_PERIODS 2 3062306a36Sopenharmony_ci#define CAPTURE_MAX_NUM_PERIODS 8 3162306a36Sopenharmony_ci#define CAPTURE_MAX_PERIOD_SIZE 4096 3262306a36Sopenharmony_ci#define CAPTURE_MIN_PERIOD_SIZE 320 3362306a36Sopenharmony_ci#define SID_MASK_DEFAULT 0xF 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Default values used if user space does not set */ 3662306a36Sopenharmony_ci#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) 3762306a36Sopenharmony_ci#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) 3862306a36Sopenharmony_ci#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) 3962306a36Sopenharmony_ci#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1) 4262306a36Sopenharmony_ci#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum stream_state { 4562306a36Sopenharmony_ci Q6ASM_STREAM_IDLE = 0, 4662306a36Sopenharmony_ci Q6ASM_STREAM_STOPPED, 4762306a36Sopenharmony_ci Q6ASM_STREAM_RUNNING, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct q6asm_dai_rtd { 5162306a36Sopenharmony_ci struct snd_pcm_substream *substream; 5262306a36Sopenharmony_ci struct snd_compr_stream *cstream; 5362306a36Sopenharmony_ci struct snd_codec codec; 5462306a36Sopenharmony_ci struct snd_dma_buffer dma_buffer; 5562306a36Sopenharmony_ci spinlock_t lock; 5662306a36Sopenharmony_ci phys_addr_t phys; 5762306a36Sopenharmony_ci unsigned int pcm_size; 5862306a36Sopenharmony_ci unsigned int pcm_count; 5962306a36Sopenharmony_ci unsigned int pcm_irq_pos; /* IRQ position */ 6062306a36Sopenharmony_ci unsigned int periods; 6162306a36Sopenharmony_ci unsigned int bytes_sent; 6262306a36Sopenharmony_ci unsigned int bytes_received; 6362306a36Sopenharmony_ci unsigned int copied_total; 6462306a36Sopenharmony_ci uint16_t bits_per_sample; 6562306a36Sopenharmony_ci uint16_t source; /* Encoding source bit mask */ 6662306a36Sopenharmony_ci struct audio_client *audio_client; 6762306a36Sopenharmony_ci uint32_t next_track_stream_id; 6862306a36Sopenharmony_ci bool next_track; 6962306a36Sopenharmony_ci uint32_t stream_id; 7062306a36Sopenharmony_ci uint16_t session_id; 7162306a36Sopenharmony_ci enum stream_state state; 7262306a36Sopenharmony_ci uint32_t initial_samples_drop; 7362306a36Sopenharmony_ci uint32_t trailing_samples_drop; 7462306a36Sopenharmony_ci bool notify_on_drain; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct q6asm_dai_data { 7862306a36Sopenharmony_ci struct snd_soc_dai_driver *dais; 7962306a36Sopenharmony_ci int num_dais; 8062306a36Sopenharmony_ci long long int sid; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct snd_pcm_hardware q6asm_dai_hardware_capture = { 8462306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | 8562306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 8662306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 8762306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 8862306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 8962306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 9062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), 9162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 9262306a36Sopenharmony_ci .rate_min = 8000, 9362306a36Sopenharmony_ci .rate_max = 48000, 9462306a36Sopenharmony_ci .channels_min = 1, 9562306a36Sopenharmony_ci .channels_max = 4, 9662306a36Sopenharmony_ci .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * 9762306a36Sopenharmony_ci CAPTURE_MAX_PERIOD_SIZE, 9862306a36Sopenharmony_ci .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 9962306a36Sopenharmony_ci .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 10062306a36Sopenharmony_ci .periods_min = CAPTURE_MIN_NUM_PERIODS, 10162306a36Sopenharmony_ci .periods_max = CAPTURE_MAX_NUM_PERIODS, 10262306a36Sopenharmony_ci .fifo_size = 0, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct snd_pcm_hardware q6asm_dai_hardware_playback = { 10662306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | 10762306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 10862306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 10962306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 11062306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 11162306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 11262306a36Sopenharmony_ci 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 = 1, 11762306a36Sopenharmony_ci .channels_max = 8, 11862306a36Sopenharmony_ci .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * 11962306a36Sopenharmony_ci PLAYBACK_MAX_PERIOD_SIZE), 12062306a36Sopenharmony_ci .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 12162306a36Sopenharmony_ci .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 12262306a36Sopenharmony_ci .periods_min = PLAYBACK_MIN_NUM_PERIODS, 12362306a36Sopenharmony_ci .periods_max = PLAYBACK_MAX_NUM_PERIODS, 12462306a36Sopenharmony_ci .fifo_size = 0, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define Q6ASM_FEDAI_DRIVER(num) { \ 12862306a36Sopenharmony_ci .playback = { \ 12962306a36Sopenharmony_ci .stream_name = "MultiMedia"#num" Playback", \ 13062306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000_192000| \ 13162306a36Sopenharmony_ci SNDRV_PCM_RATE_KNOT), \ 13262306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ 13362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), \ 13462306a36Sopenharmony_ci .channels_min = 1, \ 13562306a36Sopenharmony_ci .channels_max = 8, \ 13662306a36Sopenharmony_ci .rate_min = 8000, \ 13762306a36Sopenharmony_ci .rate_max = 192000, \ 13862306a36Sopenharmony_ci }, \ 13962306a36Sopenharmony_ci .capture = { \ 14062306a36Sopenharmony_ci .stream_name = "MultiMedia"#num" Capture", \ 14162306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000_48000| \ 14262306a36Sopenharmony_ci SNDRV_PCM_RATE_KNOT), \ 14362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ 14462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), \ 14562306a36Sopenharmony_ci .channels_min = 1, \ 14662306a36Sopenharmony_ci .channels_max = 4, \ 14762306a36Sopenharmony_ci .rate_min = 8000, \ 14862306a36Sopenharmony_ci .rate_max = 48000, \ 14962306a36Sopenharmony_ci }, \ 15062306a36Sopenharmony_ci .name = "MultiMedia"#num, \ 15162306a36Sopenharmony_ci .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \ 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* Conventional and unconventional sample rate supported */ 15562306a36Sopenharmony_cistatic unsigned int supported_sample_rates[] = { 15662306a36Sopenharmony_ci 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 15762306a36Sopenharmony_ci 88200, 96000, 176400, 192000 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct snd_pcm_hw_constraint_list constraints_sample_rates = { 16162306a36Sopenharmony_ci .count = ARRAY_SIZE(supported_sample_rates), 16262306a36Sopenharmony_ci .list = supported_sample_rates, 16362306a36Sopenharmony_ci .mask = 0, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct snd_compr_codec_caps q6asm_compr_caps = { 16762306a36Sopenharmony_ci .num_descriptors = 1, 16862306a36Sopenharmony_ci .descriptor[0].max_ch = 2, 16962306a36Sopenharmony_ci .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, 17062306a36Sopenharmony_ci 24000, 32000, 44100, 48000, 88200, 17162306a36Sopenharmony_ci 96000, 176400, 192000 }, 17262306a36Sopenharmony_ci .descriptor[0].num_sample_rates = 13, 17362306a36Sopenharmony_ci .descriptor[0].bit_rate[0] = 320, 17462306a36Sopenharmony_ci .descriptor[0].bit_rate[1] = 128, 17562306a36Sopenharmony_ci .descriptor[0].num_bitrates = 2, 17662306a36Sopenharmony_ci .descriptor[0].profiles = 0, 17762306a36Sopenharmony_ci .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, 17862306a36Sopenharmony_ci .descriptor[0].formats = 0, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void event_handler(uint32_t opcode, uint32_t token, 18262306a36Sopenharmony_ci void *payload, void *priv) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = priv; 18562306a36Sopenharmony_ci struct snd_pcm_substream *substream = prtd->substream; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci switch (opcode) { 18862306a36Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_RUN_DONE: 18962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 19062306a36Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 19162306a36Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_EOS_DONE: 19462306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { 19762306a36Sopenharmony_ci prtd->pcm_irq_pos += prtd->pcm_count; 19862306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 19962306a36Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING) 20062306a36Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 20162306a36Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_READ_DONE: 20662306a36Sopenharmony_ci prtd->pcm_irq_pos += prtd->pcm_count; 20762306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 20862306a36Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING) 20962306a36Sopenharmony_ci q6asm_read(prtd->audio_client, prtd->stream_id); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci default: 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int q6asm_dai_prepare(struct snd_soc_component *component, 21862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 22162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream); 22262306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 22362306a36Sopenharmony_ci struct q6asm_dai_data *pdata; 22462306a36Sopenharmony_ci struct device *dev = component->dev; 22562306a36Sopenharmony_ci int ret, i; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 22862306a36Sopenharmony_ci if (!pdata) 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!prtd || !prtd->audio_client) { 23262306a36Sopenharmony_ci dev_err(dev, "%s: private data null or audio client freed\n", 23362306a36Sopenharmony_ci __func__); 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci prtd->pcm_count = snd_pcm_lib_period_bytes(substream); 23862306a36Sopenharmony_ci prtd->pcm_irq_pos = 0; 23962306a36Sopenharmony_ci /* rate and channels are sent to audio driver */ 24062306a36Sopenharmony_ci if (prtd->state) { 24162306a36Sopenharmony_ci /* clear the previous setup if any */ 24262306a36Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); 24362306a36Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, 24462306a36Sopenharmony_ci prtd->audio_client); 24562306a36Sopenharmony_ci q6routing_stream_close(soc_prtd->dai_link->id, 24662306a36Sopenharmony_ci substream->stream); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, 25062306a36Sopenharmony_ci prtd->phys, 25162306a36Sopenharmony_ci (prtd->pcm_size / prtd->periods), 25262306a36Sopenharmony_ci prtd->periods); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (ret < 0) { 25562306a36Sopenharmony_ci dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", 25662306a36Sopenharmony_ci ret); 25762306a36Sopenharmony_ci return -ENOMEM; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 26162306a36Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, 26262306a36Sopenharmony_ci FORMAT_LINEAR_PCM, 26362306a36Sopenharmony_ci 0, prtd->bits_per_sample, false); 26462306a36Sopenharmony_ci } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 26562306a36Sopenharmony_ci ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, 26662306a36Sopenharmony_ci FORMAT_LINEAR_PCM, 26762306a36Sopenharmony_ci prtd->bits_per_sample); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (ret < 0) { 27162306a36Sopenharmony_ci dev_err(dev, "%s: q6asm_open_write failed\n", __func__); 27262306a36Sopenharmony_ci goto open_err; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci prtd->session_id = q6asm_get_session_id(prtd->audio_client); 27662306a36Sopenharmony_ci ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, 27762306a36Sopenharmony_ci prtd->session_id, substream->stream); 27862306a36Sopenharmony_ci if (ret) { 27962306a36Sopenharmony_ci dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret); 28062306a36Sopenharmony_ci goto routing_err; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 28462306a36Sopenharmony_ci ret = q6asm_media_format_block_multi_ch_pcm( 28562306a36Sopenharmony_ci prtd->audio_client, prtd->stream_id, 28662306a36Sopenharmony_ci runtime->rate, runtime->channels, NULL, 28762306a36Sopenharmony_ci prtd->bits_per_sample); 28862306a36Sopenharmony_ci } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 28962306a36Sopenharmony_ci ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, 29062306a36Sopenharmony_ci prtd->stream_id, 29162306a36Sopenharmony_ci runtime->rate, 29262306a36Sopenharmony_ci runtime->channels, 29362306a36Sopenharmony_ci prtd->bits_per_sample); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Queue the buffers */ 29662306a36Sopenharmony_ci for (i = 0; i < runtime->periods; i++) 29762306a36Sopenharmony_ci q6asm_read(prtd->audio_client, prtd->stream_id); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci if (ret < 0) 30162306a36Sopenharmony_ci dev_info(dev, "%s: CMD Format block failed\n", __func__); 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_RUNNING; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cirouting_err: 30862306a36Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); 30962306a36Sopenharmony_ciopen_err: 31062306a36Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); 31162306a36Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 31262306a36Sopenharmony_ci prtd->audio_client = NULL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int q6asm_dai_trigger(struct snd_soc_component *component, 31862306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int ret = 0; 32162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 32262306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci switch (cmd) { 32562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 32662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 32762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 32862306a36Sopenharmony_ci ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, 32962306a36Sopenharmony_ci 0, 0, 0); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 33262306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 33362306a36Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 33462306a36Sopenharmony_ci CMD_EOS); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 33762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 33862306a36Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 33962306a36Sopenharmony_ci CMD_PAUSE); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci ret = -EINVAL; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int q6asm_dai_open(struct snd_soc_component *component, 35062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 35362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream); 35462306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); 35562306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd; 35662306a36Sopenharmony_ci struct q6asm_dai_data *pdata; 35762306a36Sopenharmony_ci struct device *dev = component->dev; 35862306a36Sopenharmony_ci int ret = 0; 35962306a36Sopenharmony_ci int stream_id; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci stream_id = cpu_dai->driver->id; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 36462306a36Sopenharmony_ci if (!pdata) { 36562306a36Sopenharmony_ci dev_err(dev, "Drv data not found ..\n"); 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL); 37062306a36Sopenharmony_ci if (prtd == NULL) 37162306a36Sopenharmony_ci return -ENOMEM; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci prtd->substream = substream; 37462306a36Sopenharmony_ci prtd->audio_client = q6asm_audio_client_alloc(dev, 37562306a36Sopenharmony_ci (q6asm_cb)event_handler, prtd, stream_id, 37662306a36Sopenharmony_ci LEGACY_PCM_MODE); 37762306a36Sopenharmony_ci if (IS_ERR(prtd->audio_client)) { 37862306a36Sopenharmony_ci dev_info(dev, "%s: Could not allocate memory\n", __func__); 37962306a36Sopenharmony_ci ret = PTR_ERR(prtd->audio_client); 38062306a36Sopenharmony_ci kfree(prtd); 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* DSP expects stream id from 1 */ 38562306a36Sopenharmony_ci prtd->stream_id = 1; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 38862306a36Sopenharmony_ci runtime->hw = q6asm_dai_hardware_playback; 38962306a36Sopenharmony_ci else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 39062306a36Sopenharmony_ci runtime->hw = q6asm_dai_hardware_capture; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_list(runtime, 0, 39362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 39462306a36Sopenharmony_ci &constraints_sample_rates); 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci dev_info(dev, "snd_pcm_hw_constraint_list failed\n"); 39762306a36Sopenharmony_ci /* Ensure that buffer size is a multiple of period size */ 39862306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, 39962306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 40062306a36Sopenharmony_ci if (ret < 0) 40162306a36Sopenharmony_ci dev_info(dev, "snd_pcm_hw_constraint_integer failed\n"); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40462306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(runtime, 40562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 40662306a36Sopenharmony_ci PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, 40762306a36Sopenharmony_ci PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); 40862306a36Sopenharmony_ci if (ret < 0) { 40962306a36Sopenharmony_ci dev_err(dev, "constraint for buffer bytes min max ret = %d\n", 41062306a36Sopenharmony_ci ret); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 41562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 41662306a36Sopenharmony_ci if (ret < 0) { 41762306a36Sopenharmony_ci dev_err(dev, "constraint for period bytes step ret = %d\n", 41862306a36Sopenharmony_ci ret); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 42162306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 42262306a36Sopenharmony_ci if (ret < 0) { 42362306a36Sopenharmony_ci dev_err(dev, "constraint for buffer bytes step ret = %d\n", 42462306a36Sopenharmony_ci ret); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci runtime->private_data = prtd; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (pdata->sid < 0) 43562306a36Sopenharmony_ci prtd->phys = substream->dma_buffer.addr; 43662306a36Sopenharmony_ci else 43762306a36Sopenharmony_ci prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int q6asm_dai_close(struct snd_soc_component *component, 44362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 44662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream); 44762306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (prtd->audio_client) { 45062306a36Sopenharmony_ci if (prtd->state) 45162306a36Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, 45262306a36Sopenharmony_ci CMD_CLOSE); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, 45562306a36Sopenharmony_ci prtd->audio_client); 45662306a36Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 45762306a36Sopenharmony_ci prtd->audio_client = NULL; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci q6routing_stream_close(soc_prtd->dai_link->id, 46062306a36Sopenharmony_ci substream->stream); 46162306a36Sopenharmony_ci kfree(prtd); 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component, 46662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 47062306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (prtd->pcm_irq_pos >= prtd->pcm_size) 47362306a36Sopenharmony_ci prtd->pcm_irq_pos = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int q6asm_dai_hw_params(struct snd_soc_component *component, 47962306a36Sopenharmony_ci struct snd_pcm_substream *substream, 48062306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 48362306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci prtd->pcm_size = params_buffer_bytes(params); 48662306a36Sopenharmony_ci prtd->periods = params_periods(params); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci switch (params_format(params)) { 48962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 49062306a36Sopenharmony_ci prtd->bits_per_sample = 16; 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 49362306a36Sopenharmony_ci prtd->bits_per_sample = 24; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void compress_event_handler(uint32_t opcode, uint32_t token, 50162306a36Sopenharmony_ci void *payload, void *priv) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = priv; 50462306a36Sopenharmony_ci struct snd_compr_stream *substream = prtd->cstream; 50562306a36Sopenharmony_ci unsigned long flags; 50662306a36Sopenharmony_ci u32 wflags = 0; 50762306a36Sopenharmony_ci uint64_t avail; 50862306a36Sopenharmony_ci uint32_t bytes_written, bytes_to_write; 50962306a36Sopenharmony_ci bool is_last_buffer = false; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci switch (opcode) { 51262306a36Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_RUN_DONE: 51362306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 51462306a36Sopenharmony_ci if (!prtd->bytes_sent) { 51562306a36Sopenharmony_ci q6asm_stream_remove_initial_silence(prtd->audio_client, 51662306a36Sopenharmony_ci prtd->stream_id, 51762306a36Sopenharmony_ci prtd->initial_samples_drop); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 52062306a36Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 52162306a36Sopenharmony_ci prtd->bytes_sent += prtd->pcm_count; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_EOS_DONE: 52862306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 52962306a36Sopenharmony_ci if (prtd->notify_on_drain) { 53062306a36Sopenharmony_ci if (substream->partial_drain) { 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * Close old stream and make it stale, switch 53362306a36Sopenharmony_ci * the active stream now! 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci q6asm_cmd_nowait(prtd->audio_client, 53662306a36Sopenharmony_ci prtd->stream_id, 53762306a36Sopenharmony_ci CMD_CLOSE); 53862306a36Sopenharmony_ci /* 53962306a36Sopenharmony_ci * vaild stream ids start from 1, So we are 54062306a36Sopenharmony_ci * toggling this between 1 and 2. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci snd_compr_drain_notify(prtd->cstream); 54662306a36Sopenharmony_ci prtd->notify_on_drain = false; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_WRITE_DONE: 55562306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; 55862306a36Sopenharmony_ci prtd->copied_total += bytes_written; 55962306a36Sopenharmony_ci snd_compr_fragment_elapsed(substream); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (prtd->state != Q6ASM_STREAM_RUNNING) { 56262306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 56762306a36Sopenharmony_ci if (avail > prtd->pcm_count) { 56862306a36Sopenharmony_ci bytes_to_write = prtd->pcm_count; 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci if (substream->partial_drain || prtd->notify_on_drain) 57162306a36Sopenharmony_ci is_last_buffer = true; 57262306a36Sopenharmony_ci bytes_to_write = avail; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (bytes_to_write) { 57662306a36Sopenharmony_ci if (substream->partial_drain && is_last_buffer) { 57762306a36Sopenharmony_ci wflags |= ASM_LAST_BUFFER_FLAG; 57862306a36Sopenharmony_ci q6asm_stream_remove_trailing_silence(prtd->audio_client, 57962306a36Sopenharmony_ci prtd->stream_id, 58062306a36Sopenharmony_ci prtd->trailing_samples_drop); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 58462306a36Sopenharmony_ci bytes_to_write, 0, 0, wflags); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (prtd->notify_on_drain && is_last_buffer) 59062306a36Sopenharmony_ci q6asm_cmd_nowait(prtd->audio_client, 59162306a36Sopenharmony_ci prtd->stream_id, CMD_EOS); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci default: 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int q6asm_dai_compr_open(struct snd_soc_component *component, 60262306a36Sopenharmony_ci struct snd_compr_stream *stream) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 60562306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 60662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 60762306a36Sopenharmony_ci struct q6asm_dai_data *pdata; 60862306a36Sopenharmony_ci struct device *dev = component->dev; 60962306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd; 61062306a36Sopenharmony_ci int stream_id, size, ret; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci stream_id = cpu_dai->driver->id; 61362306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 61462306a36Sopenharmony_ci if (!pdata) { 61562306a36Sopenharmony_ci dev_err(dev, "Drv data not found ..\n"); 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 62062306a36Sopenharmony_ci if (!prtd) 62162306a36Sopenharmony_ci return -ENOMEM; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* DSP expects stream id from 1 */ 62462306a36Sopenharmony_ci prtd->stream_id = 1; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci prtd->cstream = stream; 62762306a36Sopenharmony_ci prtd->audio_client = q6asm_audio_client_alloc(dev, 62862306a36Sopenharmony_ci (q6asm_cb)compress_event_handler, 62962306a36Sopenharmony_ci prtd, stream_id, LEGACY_PCM_MODE); 63062306a36Sopenharmony_ci if (IS_ERR(prtd->audio_client)) { 63162306a36Sopenharmony_ci dev_err(dev, "Could not allocate memory\n"); 63262306a36Sopenharmony_ci ret = PTR_ERR(prtd->audio_client); 63362306a36Sopenharmony_ci goto free_prtd; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * 63762306a36Sopenharmony_ci COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 63862306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, 63962306a36Sopenharmony_ci &prtd->dma_buffer); 64062306a36Sopenharmony_ci if (ret) { 64162306a36Sopenharmony_ci dev_err(dev, "Cannot allocate buffer(s)\n"); 64262306a36Sopenharmony_ci goto free_client; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (pdata->sid < 0) 64662306a36Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr; 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); 65162306a36Sopenharmony_ci spin_lock_init(&prtd->lock); 65262306a36Sopenharmony_ci runtime->private_data = prtd; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cifree_client: 65762306a36Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 65862306a36Sopenharmony_cifree_prtd: 65962306a36Sopenharmony_ci kfree(prtd); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int q6asm_dai_compr_free(struct snd_soc_component *component, 66562306a36Sopenharmony_ci struct snd_compr_stream *stream) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 66862306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 66962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (prtd->audio_client) { 67262306a36Sopenharmony_ci if (prtd->state) { 67362306a36Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, 67462306a36Sopenharmony_ci CMD_CLOSE); 67562306a36Sopenharmony_ci if (prtd->next_track_stream_id) { 67662306a36Sopenharmony_ci q6asm_cmd(prtd->audio_client, 67762306a36Sopenharmony_ci prtd->next_track_stream_id, 67862306a36Sopenharmony_ci CMD_CLOSE); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci snd_dma_free_pages(&prtd->dma_buffer); 68362306a36Sopenharmony_ci q6asm_unmap_memory_regions(stream->direction, 68462306a36Sopenharmony_ci prtd->audio_client); 68562306a36Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 68662306a36Sopenharmony_ci prtd->audio_client = NULL; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci q6routing_stream_close(rtd->dai_link->id, stream->direction); 68962306a36Sopenharmony_ci kfree(prtd); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return 0; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, 69562306a36Sopenharmony_ci struct snd_compr_stream *stream, 69662306a36Sopenharmony_ci struct snd_codec *codec, 69762306a36Sopenharmony_ci int stream_id) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 70062306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 70162306a36Sopenharmony_ci struct q6asm_flac_cfg flac_cfg; 70262306a36Sopenharmony_ci struct q6asm_wma_cfg wma_cfg; 70362306a36Sopenharmony_ci struct q6asm_alac_cfg alac_cfg; 70462306a36Sopenharmony_ci struct q6asm_ape_cfg ape_cfg; 70562306a36Sopenharmony_ci unsigned int wma_v9 = 0; 70662306a36Sopenharmony_ci struct device *dev = component->dev; 70762306a36Sopenharmony_ci int ret; 70862306a36Sopenharmony_ci union snd_codec_options *codec_options; 70962306a36Sopenharmony_ci struct snd_dec_flac *flac; 71062306a36Sopenharmony_ci struct snd_dec_wma *wma; 71162306a36Sopenharmony_ci struct snd_dec_alac *alac; 71262306a36Sopenharmony_ci struct snd_dec_ape *ape; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci codec_options = &(prtd->codec.options); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci memcpy(&prtd->codec, codec, sizeof(*codec)); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci switch (codec->id) { 71962306a36Sopenharmony_ci case SND_AUDIOCODEC_FLAC: 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); 72262306a36Sopenharmony_ci flac = &codec_options->flac_d; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci flac_cfg.ch_cfg = codec->ch_in; 72562306a36Sopenharmony_ci flac_cfg.sample_rate = codec->sample_rate; 72662306a36Sopenharmony_ci flac_cfg.stream_info_present = 1; 72762306a36Sopenharmony_ci flac_cfg.sample_size = flac->sample_size; 72862306a36Sopenharmony_ci flac_cfg.min_blk_size = flac->min_blk_size; 72962306a36Sopenharmony_ci flac_cfg.max_blk_size = flac->max_blk_size; 73062306a36Sopenharmony_ci flac_cfg.max_frame_size = flac->max_frame_size; 73162306a36Sopenharmony_ci flac_cfg.min_frame_size = flac->min_frame_size; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = q6asm_stream_media_format_block_flac(prtd->audio_client, 73462306a36Sopenharmony_ci stream_id, 73562306a36Sopenharmony_ci &flac_cfg); 73662306a36Sopenharmony_ci if (ret < 0) { 73762306a36Sopenharmony_ci dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); 73862306a36Sopenharmony_ci return -EIO; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci case SND_AUDIOCODEC_WMA: 74362306a36Sopenharmony_ci wma = &codec_options->wma_d; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci wma_cfg.sample_rate = codec->sample_rate; 74862306a36Sopenharmony_ci wma_cfg.num_channels = codec->ch_in; 74962306a36Sopenharmony_ci wma_cfg.bytes_per_sec = codec->bit_rate / 8; 75062306a36Sopenharmony_ci wma_cfg.block_align = codec->align; 75162306a36Sopenharmony_ci wma_cfg.bits_per_sample = prtd->bits_per_sample; 75262306a36Sopenharmony_ci wma_cfg.enc_options = wma->encoder_option; 75362306a36Sopenharmony_ci wma_cfg.adv_enc_options = wma->adv_encoder_option; 75462306a36Sopenharmony_ci wma_cfg.adv_enc_options2 = wma->adv_encoder_option2; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (wma_cfg.num_channels == 1) 75762306a36Sopenharmony_ci wma_cfg.channel_mask = 4; /* Mono Center */ 75862306a36Sopenharmony_ci else if (wma_cfg.num_channels == 2) 75962306a36Sopenharmony_ci wma_cfg.channel_mask = 3; /* Stereo FL/FR */ 76062306a36Sopenharmony_ci else 76162306a36Sopenharmony_ci return -EINVAL; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* check the codec profile */ 76462306a36Sopenharmony_ci switch (codec->profile) { 76562306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9: 76662306a36Sopenharmony_ci wma_cfg.fmtag = 0x161; 76762306a36Sopenharmony_ci wma_v9 = 1; 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA10: 77162306a36Sopenharmony_ci wma_cfg.fmtag = 0x166; 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_PRO: 77562306a36Sopenharmony_ci wma_cfg.fmtag = 0x162; 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_LOSSLESS: 77962306a36Sopenharmony_ci wma_cfg.fmtag = 0x163; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci case SND_AUDIOPROFILE_WMA10_LOSSLESS: 78362306a36Sopenharmony_ci wma_cfg.fmtag = 0x167; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci default: 78762306a36Sopenharmony_ci dev_err(dev, "Unknown WMA profile:%x\n", 78862306a36Sopenharmony_ci codec->profile); 78962306a36Sopenharmony_ci return -EIO; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (wma_v9) 79362306a36Sopenharmony_ci ret = q6asm_stream_media_format_block_wma_v9( 79462306a36Sopenharmony_ci prtd->audio_client, stream_id, 79562306a36Sopenharmony_ci &wma_cfg); 79662306a36Sopenharmony_ci else 79762306a36Sopenharmony_ci ret = q6asm_stream_media_format_block_wma_v10( 79862306a36Sopenharmony_ci prtd->audio_client, stream_id, 79962306a36Sopenharmony_ci &wma_cfg); 80062306a36Sopenharmony_ci if (ret < 0) { 80162306a36Sopenharmony_ci dev_err(dev, "WMA9 CMD failed:%d\n", ret); 80262306a36Sopenharmony_ci return -EIO; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci case SND_AUDIOCODEC_ALAC: 80762306a36Sopenharmony_ci memset(&alac_cfg, 0x0, sizeof(alac_cfg)); 80862306a36Sopenharmony_ci alac = &codec_options->alac_d; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci alac_cfg.sample_rate = codec->sample_rate; 81162306a36Sopenharmony_ci alac_cfg.avg_bit_rate = codec->bit_rate; 81262306a36Sopenharmony_ci alac_cfg.bit_depth = prtd->bits_per_sample; 81362306a36Sopenharmony_ci alac_cfg.num_channels = codec->ch_in; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci alac_cfg.frame_length = alac->frame_length; 81662306a36Sopenharmony_ci alac_cfg.pb = alac->pb; 81762306a36Sopenharmony_ci alac_cfg.mb = alac->mb; 81862306a36Sopenharmony_ci alac_cfg.kb = alac->kb; 81962306a36Sopenharmony_ci alac_cfg.max_run = alac->max_run; 82062306a36Sopenharmony_ci alac_cfg.compatible_version = alac->compatible_version; 82162306a36Sopenharmony_ci alac_cfg.max_frame_bytes = alac->max_frame_bytes; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci switch (codec->ch_in) { 82462306a36Sopenharmony_ci case 1: 82562306a36Sopenharmony_ci alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci case 2: 82862306a36Sopenharmony_ci alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO; 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci ret = q6asm_stream_media_format_block_alac(prtd->audio_client, 83262306a36Sopenharmony_ci stream_id, 83362306a36Sopenharmony_ci &alac_cfg); 83462306a36Sopenharmony_ci if (ret < 0) { 83562306a36Sopenharmony_ci dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); 83662306a36Sopenharmony_ci return -EIO; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci case SND_AUDIOCODEC_APE: 84162306a36Sopenharmony_ci memset(&ape_cfg, 0x0, sizeof(ape_cfg)); 84262306a36Sopenharmony_ci ape = &codec_options->ape_d; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ape_cfg.sample_rate = codec->sample_rate; 84562306a36Sopenharmony_ci ape_cfg.num_channels = codec->ch_in; 84662306a36Sopenharmony_ci ape_cfg.bits_per_sample = prtd->bits_per_sample; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ape_cfg.compatible_version = ape->compatible_version; 84962306a36Sopenharmony_ci ape_cfg.compression_level = ape->compression_level; 85062306a36Sopenharmony_ci ape_cfg.format_flags = ape->format_flags; 85162306a36Sopenharmony_ci ape_cfg.blocks_per_frame = ape->blocks_per_frame; 85262306a36Sopenharmony_ci ape_cfg.final_frame_blocks = ape->final_frame_blocks; 85362306a36Sopenharmony_ci ape_cfg.total_frames = ape->total_frames; 85462306a36Sopenharmony_ci ape_cfg.seek_table_present = ape->seek_table_present; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = q6asm_stream_media_format_block_ape(prtd->audio_client, 85762306a36Sopenharmony_ci stream_id, 85862306a36Sopenharmony_ci &ape_cfg); 85962306a36Sopenharmony_ci if (ret < 0) { 86062306a36Sopenharmony_ci dev_err(dev, "APE CMD Format block failed:%d\n", ret); 86162306a36Sopenharmony_ci return -EIO; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci default: 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int q6asm_dai_compr_set_params(struct snd_soc_component *component, 87362306a36Sopenharmony_ci struct snd_compr_stream *stream, 87462306a36Sopenharmony_ci struct snd_compr_params *params) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 87762306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 87862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 87962306a36Sopenharmony_ci int dir = stream->direction; 88062306a36Sopenharmony_ci struct q6asm_dai_data *pdata; 88162306a36Sopenharmony_ci struct device *dev = component->dev; 88262306a36Sopenharmony_ci int ret; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 88562306a36Sopenharmony_ci if (!pdata) 88662306a36Sopenharmony_ci return -EINVAL; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (!prtd || !prtd->audio_client) { 88962306a36Sopenharmony_ci dev_err(dev, "private data null or audio client freed\n"); 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci prtd->periods = runtime->fragments; 89462306a36Sopenharmony_ci prtd->pcm_count = runtime->fragment_size; 89562306a36Sopenharmony_ci prtd->pcm_size = runtime->fragments * runtime->fragment_size; 89662306a36Sopenharmony_ci prtd->bits_per_sample = 16; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (dir == SND_COMPRESS_PLAYBACK) { 89962306a36Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, 90062306a36Sopenharmony_ci params->codec.profile, prtd->bits_per_sample, 90162306a36Sopenharmony_ci true); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (ret < 0) { 90462306a36Sopenharmony_ci dev_err(dev, "q6asm_open_write failed\n"); 90562306a36Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 90662306a36Sopenharmony_ci prtd->audio_client = NULL; 90762306a36Sopenharmony_ci return ret; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci prtd->session_id = q6asm_get_session_id(prtd->audio_client); 91262306a36Sopenharmony_ci ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, 91362306a36Sopenharmony_ci prtd->session_id, dir); 91462306a36Sopenharmony_ci if (ret) { 91562306a36Sopenharmony_ci dev_err(dev, "Stream reg failed ret:%d\n", ret); 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = __q6asm_dai_compr_set_codec_params(component, stream, 92062306a36Sopenharmony_ci ¶ms->codec, 92162306a36Sopenharmony_ci prtd->stream_id); 92262306a36Sopenharmony_ci if (ret) { 92362306a36Sopenharmony_ci dev_err(dev, "codec param setup failed ret:%d\n", ret); 92462306a36Sopenharmony_ci return ret; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, 92862306a36Sopenharmony_ci (prtd->pcm_size / prtd->periods), 92962306a36Sopenharmony_ci prtd->periods); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (ret < 0) { 93262306a36Sopenharmony_ci dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); 93362306a36Sopenharmony_ci return -ENOMEM; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_RUNNING; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, 94262306a36Sopenharmony_ci struct snd_compr_stream *stream, 94362306a36Sopenharmony_ci struct snd_compr_metadata *metadata) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 94662306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 94762306a36Sopenharmony_ci int ret = 0; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci switch (metadata->key) { 95062306a36Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_PADDING: 95162306a36Sopenharmony_ci prtd->trailing_samples_drop = metadata->value[0]; 95262306a36Sopenharmony_ci break; 95362306a36Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_DELAY: 95462306a36Sopenharmony_ci prtd->initial_samples_drop = metadata->value[0]; 95562306a36Sopenharmony_ci if (prtd->next_track_stream_id) { 95662306a36Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, 95762306a36Sopenharmony_ci prtd->next_track_stream_id, 95862306a36Sopenharmony_ci prtd->codec.id, 95962306a36Sopenharmony_ci prtd->codec.profile, 96062306a36Sopenharmony_ci prtd->bits_per_sample, 96162306a36Sopenharmony_ci true); 96262306a36Sopenharmony_ci if (ret < 0) { 96362306a36Sopenharmony_ci dev_err(component->dev, "q6asm_open_write failed\n"); 96462306a36Sopenharmony_ci return ret; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci ret = __q6asm_dai_compr_set_codec_params(component, stream, 96762306a36Sopenharmony_ci &prtd->codec, 96862306a36Sopenharmony_ci prtd->next_track_stream_id); 96962306a36Sopenharmony_ci if (ret < 0) { 97062306a36Sopenharmony_ci dev_err(component->dev, "q6asm_open_write failed\n"); 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci ret = q6asm_stream_remove_initial_silence(prtd->audio_client, 97562306a36Sopenharmony_ci prtd->next_track_stream_id, 97662306a36Sopenharmony_ci prtd->initial_samples_drop); 97762306a36Sopenharmony_ci prtd->next_track_stream_id = 0; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci default: 98362306a36Sopenharmony_ci ret = -EINVAL; 98462306a36Sopenharmony_ci break; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return ret; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int q6asm_dai_compr_trigger(struct snd_soc_component *component, 99162306a36Sopenharmony_ci struct snd_compr_stream *stream, int cmd) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 99462306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 99562306a36Sopenharmony_ci int ret = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci switch (cmd) { 99862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 99962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 100062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 100162306a36Sopenharmony_ci ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, 100262306a36Sopenharmony_ci 0, 0, 0); 100362306a36Sopenharmony_ci break; 100462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 100562306a36Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 100662306a36Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 100762306a36Sopenharmony_ci CMD_EOS); 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 101062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 101162306a36Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 101262306a36Sopenharmony_ci CMD_PAUSE); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case SND_COMPR_TRIGGER_NEXT_TRACK: 101562306a36Sopenharmony_ci prtd->next_track = true; 101662306a36Sopenharmony_ci prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case SND_COMPR_TRIGGER_DRAIN: 101962306a36Sopenharmony_ci case SND_COMPR_TRIGGER_PARTIAL_DRAIN: 102062306a36Sopenharmony_ci prtd->notify_on_drain = true; 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci default: 102362306a36Sopenharmony_ci ret = -EINVAL; 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return ret; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int q6asm_dai_compr_pointer(struct snd_soc_component *component, 103162306a36Sopenharmony_ci struct snd_compr_stream *stream, 103262306a36Sopenharmony_ci struct snd_compr_tstamp *tstamp) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 103562306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 103662306a36Sopenharmony_ci unsigned long flags; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci tstamp->copied_total = prtd->copied_total; 104162306a36Sopenharmony_ci tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci return 0; 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic int q6asm_compr_copy(struct snd_soc_component *component, 104962306a36Sopenharmony_ci struct snd_compr_stream *stream, char __user *buf, 105062306a36Sopenharmony_ci size_t count) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 105362306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 105462306a36Sopenharmony_ci unsigned long flags; 105562306a36Sopenharmony_ci u32 wflags = 0; 105662306a36Sopenharmony_ci int avail, bytes_in_flight = 0; 105762306a36Sopenharmony_ci void *dstn; 105862306a36Sopenharmony_ci size_t copy; 105962306a36Sopenharmony_ci u32 app_pointer; 106062306a36Sopenharmony_ci u32 bytes_received; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci bytes_received = prtd->bytes_received; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /** 106562306a36Sopenharmony_ci * Make sure that next track data pointer is aligned at 32 bit boundary 106662306a36Sopenharmony_ci * This is a Mandatory requirement from DSP data buffers alignment 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ci if (prtd->next_track) 106962306a36Sopenharmony_ci bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci app_pointer = bytes_received/prtd->pcm_size; 107262306a36Sopenharmony_ci app_pointer = bytes_received - (app_pointer * prtd->pcm_size); 107362306a36Sopenharmony_ci dstn = prtd->dma_buffer.area + app_pointer; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (count < prtd->pcm_size - app_pointer) { 107662306a36Sopenharmony_ci if (copy_from_user(dstn, buf, count)) 107762306a36Sopenharmony_ci return -EFAULT; 107862306a36Sopenharmony_ci } else { 107962306a36Sopenharmony_ci copy = prtd->pcm_size - app_pointer; 108062306a36Sopenharmony_ci if (copy_from_user(dstn, buf, copy)) 108162306a36Sopenharmony_ci return -EFAULT; 108262306a36Sopenharmony_ci if (copy_from_user(prtd->dma_buffer.area, buf + copy, 108362306a36Sopenharmony_ci count - copy)) 108462306a36Sopenharmony_ci return -EFAULT; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci bytes_in_flight = prtd->bytes_received - prtd->copied_total; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (prtd->next_track) { 109262306a36Sopenharmony_ci prtd->next_track = false; 109362306a36Sopenharmony_ci prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); 109462306a36Sopenharmony_ci prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci prtd->bytes_received = bytes_received + count; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* Kick off the data to dsp if its starving!! */ 110062306a36Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { 110162306a36Sopenharmony_ci uint32_t bytes_to_write = prtd->pcm_count; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (avail < prtd->pcm_count) 110662306a36Sopenharmony_ci bytes_to_write = avail; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 110962306a36Sopenharmony_ci bytes_to_write, 0, 0, wflags); 111062306a36Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return count; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int q6asm_dai_compr_mmap(struct snd_soc_component *component, 111962306a36Sopenharmony_ci struct snd_compr_stream *stream, 112062306a36Sopenharmony_ci struct vm_area_struct *vma) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 112362306a36Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 112462306a36Sopenharmony_ci struct device *dev = component->dev; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return dma_mmap_coherent(dev, vma, 112762306a36Sopenharmony_ci prtd->dma_buffer.area, prtd->dma_buffer.addr, 112862306a36Sopenharmony_ci prtd->dma_buffer.bytes); 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic int q6asm_dai_compr_get_caps(struct snd_soc_component *component, 113262306a36Sopenharmony_ci struct snd_compr_stream *stream, 113362306a36Sopenharmony_ci struct snd_compr_caps *caps) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci caps->direction = SND_COMPRESS_PLAYBACK; 113662306a36Sopenharmony_ci caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; 113762306a36Sopenharmony_ci caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; 113862306a36Sopenharmony_ci caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; 113962306a36Sopenharmony_ci caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 114062306a36Sopenharmony_ci caps->num_codecs = 5; 114162306a36Sopenharmony_ci caps->codecs[0] = SND_AUDIOCODEC_MP3; 114262306a36Sopenharmony_ci caps->codecs[1] = SND_AUDIOCODEC_FLAC; 114362306a36Sopenharmony_ci caps->codecs[2] = SND_AUDIOCODEC_WMA; 114462306a36Sopenharmony_ci caps->codecs[3] = SND_AUDIOCODEC_ALAC; 114562306a36Sopenharmony_ci caps->codecs[4] = SND_AUDIOCODEC_APE; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return 0; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component, 115162306a36Sopenharmony_ci struct snd_compr_stream *stream, 115262306a36Sopenharmony_ci struct snd_compr_codec_caps *codec) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci switch (codec->codec) { 115562306a36Sopenharmony_ci case SND_AUDIOCODEC_MP3: 115662306a36Sopenharmony_ci *codec = q6asm_compr_caps; 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci default: 115962306a36Sopenharmony_ci break; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic const struct snd_compress_ops q6asm_dai_compress_ops = { 116662306a36Sopenharmony_ci .open = q6asm_dai_compr_open, 116762306a36Sopenharmony_ci .free = q6asm_dai_compr_free, 116862306a36Sopenharmony_ci .set_params = q6asm_dai_compr_set_params, 116962306a36Sopenharmony_ci .set_metadata = q6asm_dai_compr_set_metadata, 117062306a36Sopenharmony_ci .pointer = q6asm_dai_compr_pointer, 117162306a36Sopenharmony_ci .trigger = q6asm_dai_compr_trigger, 117262306a36Sopenharmony_ci .get_caps = q6asm_dai_compr_get_caps, 117362306a36Sopenharmony_ci .get_codec_caps = q6asm_dai_compr_get_codec_caps, 117462306a36Sopenharmony_ci .mmap = q6asm_dai_compr_mmap, 117562306a36Sopenharmony_ci .copy = q6asm_compr_copy, 117662306a36Sopenharmony_ci}; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic int q6asm_dai_pcm_new(struct snd_soc_component *component, 117962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 118262306a36Sopenharmony_ci size_t size = q6asm_dai_hardware_playback.buffer_bytes_max; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 118562306a36Sopenharmony_ci component->dev, size); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget q6asm_dapm_widgets[] = { 118962306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, SND_SOC_NOPM, 0, 0), 119062306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, SND_SOC_NOPM, 0, 0), 119162306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, SND_SOC_NOPM, 0, 0), 119262306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, SND_SOC_NOPM, 0, 0), 119362306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, SND_SOC_NOPM, 0, 0), 119462306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, SND_SOC_NOPM, 0, 0), 119562306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, SND_SOC_NOPM, 0, 0), 119662306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, SND_SOC_NOPM, 0, 0), 119762306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, SND_SOC_NOPM, 0, 0), 119862306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, SND_SOC_NOPM, 0, 0), 119962306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, SND_SOC_NOPM, 0, 0), 120062306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, SND_SOC_NOPM, 0, 0), 120162306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, SND_SOC_NOPM, 0, 0), 120262306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, SND_SOC_NOPM, 0, 0), 120362306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, SND_SOC_NOPM, 0, 0), 120462306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, SND_SOC_NOPM, 0, 0), 120562306a36Sopenharmony_ci}; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic const struct snd_soc_component_driver q6asm_fe_dai_component = { 120862306a36Sopenharmony_ci .name = DRV_NAME, 120962306a36Sopenharmony_ci .open = q6asm_dai_open, 121062306a36Sopenharmony_ci .hw_params = q6asm_dai_hw_params, 121162306a36Sopenharmony_ci .close = q6asm_dai_close, 121262306a36Sopenharmony_ci .prepare = q6asm_dai_prepare, 121362306a36Sopenharmony_ci .trigger = q6asm_dai_trigger, 121462306a36Sopenharmony_ci .pointer = q6asm_dai_pointer, 121562306a36Sopenharmony_ci .pcm_construct = q6asm_dai_pcm_new, 121662306a36Sopenharmony_ci .compress_ops = &q6asm_dai_compress_ops, 121762306a36Sopenharmony_ci .dapm_widgets = q6asm_dapm_widgets, 121862306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets), 121962306a36Sopenharmony_ci .legacy_dai_naming = 1, 122062306a36Sopenharmony_ci}; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic struct snd_soc_dai_driver q6asm_fe_dais_template[] = { 122362306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(1), 122462306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(2), 122562306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(3), 122662306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(4), 122762306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(5), 122862306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(6), 122962306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(7), 123062306a36Sopenharmony_ci Q6ASM_FEDAI_DRIVER(8), 123162306a36Sopenharmony_ci}; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops q6asm_dai_ops = { 123462306a36Sopenharmony_ci .compress_new = snd_soc_new_compress, 123562306a36Sopenharmony_ci}; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic int of_q6asm_parse_dai_data(struct device *dev, 123862306a36Sopenharmony_ci struct q6asm_dai_data *pdata) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 124162306a36Sopenharmony_ci struct snd_soc_pcm_stream empty_stream; 124262306a36Sopenharmony_ci struct device_node *node; 124362306a36Sopenharmony_ci int ret, id, dir, idx = 0; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci pdata->num_dais = of_get_child_count(dev->of_node); 124762306a36Sopenharmony_ci if (!pdata->num_dais) { 124862306a36Sopenharmony_ci dev_err(dev, "No dais found in DT\n"); 124962306a36Sopenharmony_ci return -EINVAL; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv), 125362306a36Sopenharmony_ci GFP_KERNEL); 125462306a36Sopenharmony_ci if (!pdata->dais) 125562306a36Sopenharmony_ci return -ENOMEM; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci memset(&empty_stream, 0, sizeof(empty_stream)); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 126062306a36Sopenharmony_ci ret = of_property_read_u32(node, "reg", &id); 126162306a36Sopenharmony_ci if (ret || id >= MAX_SESSIONS || id < 0) { 126262306a36Sopenharmony_ci dev_err(dev, "valid dai id not found:%d\n", ret); 126362306a36Sopenharmony_ci continue; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci dai_drv = &pdata->dais[idx++]; 126762306a36Sopenharmony_ci *dai_drv = q6asm_fe_dais_template[id]; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci ret = of_property_read_u32(node, "direction", &dir); 127062306a36Sopenharmony_ci if (ret) 127162306a36Sopenharmony_ci continue; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (dir == Q6ASM_DAI_RX) 127462306a36Sopenharmony_ci dai_drv->capture = empty_stream; 127562306a36Sopenharmony_ci else if (dir == Q6ASM_DAI_TX) 127662306a36Sopenharmony_ci dai_drv->playback = empty_stream; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (of_property_read_bool(node, "is-compress-dai")) 127962306a36Sopenharmony_ci dai_drv->ops = &q6asm_dai_ops; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic int q6asm_dai_probe(struct platform_device *pdev) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 128862306a36Sopenharmony_ci struct device_node *node = dev->of_node; 128962306a36Sopenharmony_ci struct of_phandle_args args; 129062306a36Sopenharmony_ci struct q6asm_dai_data *pdata; 129162306a36Sopenharmony_ci int rc; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 129462306a36Sopenharmony_ci if (!pdata) 129562306a36Sopenharmony_ci return -ENOMEM; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); 129862306a36Sopenharmony_ci if (rc < 0) 129962306a36Sopenharmony_ci pdata->sid = -1; 130062306a36Sopenharmony_ci else 130162306a36Sopenharmony_ci pdata->sid = args.args[0] & SID_MASK_DEFAULT; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci dev_set_drvdata(dev, pdata); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci rc = of_q6asm_parse_dai_data(dev, pdata); 130662306a36Sopenharmony_ci if (rc) 130762306a36Sopenharmony_ci return rc; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, 131062306a36Sopenharmony_ci pdata->dais, pdata->num_dais); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci#ifdef CONFIG_OF 131462306a36Sopenharmony_cistatic const struct of_device_id q6asm_dai_device_id[] = { 131562306a36Sopenharmony_ci { .compatible = "qcom,q6asm-dais" }, 131662306a36Sopenharmony_ci {}, 131762306a36Sopenharmony_ci}; 131862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6asm_dai_device_id); 131962306a36Sopenharmony_ci#endif 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic struct platform_driver q6asm_dai_platform_driver = { 132262306a36Sopenharmony_ci .driver = { 132362306a36Sopenharmony_ci .name = "q6asm-dai", 132462306a36Sopenharmony_ci .of_match_table = of_match_ptr(q6asm_dai_device_id), 132562306a36Sopenharmony_ci }, 132662306a36Sopenharmony_ci .probe = q6asm_dai_probe, 132762306a36Sopenharmony_ci}; 132862306a36Sopenharmony_cimodule_platform_driver(q6asm_dai_platform_driver); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ciMODULE_DESCRIPTION("Q6ASM dai driver"); 133162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1332