18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/err.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <sound/soc.h> 118c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h> 128c2ecf20Sopenharmony_ci#include <sound/pcm.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <sound/compress_driver.h> 158c2ecf20Sopenharmony_ci#include <asm/dma.h> 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 198c2ecf20Sopenharmony_ci#include "q6asm.h" 208c2ecf20Sopenharmony_ci#include "q6routing.h" 218c2ecf20Sopenharmony_ci#include "q6dsp-errno.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRV_NAME "q6asm-fe-dai" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PLAYBACK_MIN_NUM_PERIODS 2 268c2ecf20Sopenharmony_ci#define PLAYBACK_MAX_NUM_PERIODS 8 278c2ecf20Sopenharmony_ci#define PLAYBACK_MAX_PERIOD_SIZE 65536 288c2ecf20Sopenharmony_ci#define PLAYBACK_MIN_PERIOD_SIZE 128 298c2ecf20Sopenharmony_ci#define CAPTURE_MIN_NUM_PERIODS 2 308c2ecf20Sopenharmony_ci#define CAPTURE_MAX_NUM_PERIODS 8 318c2ecf20Sopenharmony_ci#define CAPTURE_MAX_PERIOD_SIZE 4096 328c2ecf20Sopenharmony_ci#define CAPTURE_MIN_PERIOD_SIZE 320 338c2ecf20Sopenharmony_ci#define SID_MASK_DEFAULT 0xF 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Default values used if user space does not set */ 368c2ecf20Sopenharmony_ci#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) 378c2ecf20Sopenharmony_ci#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) 388c2ecf20Sopenharmony_ci#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) 398c2ecf20Sopenharmony_ci#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1) 428c2ecf20Sopenharmony_ci#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cienum stream_state { 458c2ecf20Sopenharmony_ci Q6ASM_STREAM_IDLE = 0, 468c2ecf20Sopenharmony_ci Q6ASM_STREAM_STOPPED, 478c2ecf20Sopenharmony_ci Q6ASM_STREAM_RUNNING, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct q6asm_dai_rtd { 518c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 528c2ecf20Sopenharmony_ci struct snd_compr_stream *cstream; 538c2ecf20Sopenharmony_ci struct snd_codec codec; 548c2ecf20Sopenharmony_ci struct snd_dma_buffer dma_buffer; 558c2ecf20Sopenharmony_ci spinlock_t lock; 568c2ecf20Sopenharmony_ci phys_addr_t phys; 578c2ecf20Sopenharmony_ci unsigned int pcm_size; 588c2ecf20Sopenharmony_ci unsigned int pcm_count; 598c2ecf20Sopenharmony_ci unsigned int pcm_irq_pos; /* IRQ position */ 608c2ecf20Sopenharmony_ci unsigned int periods; 618c2ecf20Sopenharmony_ci unsigned int bytes_sent; 628c2ecf20Sopenharmony_ci unsigned int bytes_received; 638c2ecf20Sopenharmony_ci unsigned int copied_total; 648c2ecf20Sopenharmony_ci uint16_t bits_per_sample; 658c2ecf20Sopenharmony_ci uint16_t source; /* Encoding source bit mask */ 668c2ecf20Sopenharmony_ci struct audio_client *audio_client; 678c2ecf20Sopenharmony_ci uint32_t next_track_stream_id; 688c2ecf20Sopenharmony_ci bool next_track; 698c2ecf20Sopenharmony_ci uint32_t stream_id; 708c2ecf20Sopenharmony_ci uint16_t session_id; 718c2ecf20Sopenharmony_ci enum stream_state state; 728c2ecf20Sopenharmony_ci uint32_t initial_samples_drop; 738c2ecf20Sopenharmony_ci uint32_t trailing_samples_drop; 748c2ecf20Sopenharmony_ci bool notify_on_drain; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct q6asm_dai_data { 788c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dais; 798c2ecf20Sopenharmony_ci int num_dais; 808c2ecf20Sopenharmony_ci long long int sid; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware q6asm_dai_hardware_capture = { 848c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | 858c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 868c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 878c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 888c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 898c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 908c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), 918c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 928c2ecf20Sopenharmony_ci .rate_min = 8000, 938c2ecf20Sopenharmony_ci .rate_max = 48000, 948c2ecf20Sopenharmony_ci .channels_min = 1, 958c2ecf20Sopenharmony_ci .channels_max = 4, 968c2ecf20Sopenharmony_ci .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * 978c2ecf20Sopenharmony_ci CAPTURE_MAX_PERIOD_SIZE, 988c2ecf20Sopenharmony_ci .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 998c2ecf20Sopenharmony_ci .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 1008c2ecf20Sopenharmony_ci .periods_min = CAPTURE_MIN_NUM_PERIODS, 1018c2ecf20Sopenharmony_ci .periods_max = CAPTURE_MAX_NUM_PERIODS, 1028c2ecf20Sopenharmony_ci .fifo_size = 0, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic struct snd_pcm_hardware q6asm_dai_hardware_playback = { 1068c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | 1078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 1088c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 1098c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 1108c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 1118c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 1128c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), 1138c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 1148c2ecf20Sopenharmony_ci .rate_min = 8000, 1158c2ecf20Sopenharmony_ci .rate_max = 192000, 1168c2ecf20Sopenharmony_ci .channels_min = 1, 1178c2ecf20Sopenharmony_ci .channels_max = 8, 1188c2ecf20Sopenharmony_ci .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * 1198c2ecf20Sopenharmony_ci PLAYBACK_MAX_PERIOD_SIZE), 1208c2ecf20Sopenharmony_ci .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 1218c2ecf20Sopenharmony_ci .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 1228c2ecf20Sopenharmony_ci .periods_min = PLAYBACK_MIN_NUM_PERIODS, 1238c2ecf20Sopenharmony_ci .periods_max = PLAYBACK_MAX_NUM_PERIODS, 1248c2ecf20Sopenharmony_ci .fifo_size = 0, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define Q6ASM_FEDAI_DRIVER(num) { \ 1288c2ecf20Sopenharmony_ci .playback = { \ 1298c2ecf20Sopenharmony_ci .stream_name = "MultiMedia"#num" Playback", \ 1308c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000_192000| \ 1318c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_KNOT), \ 1328c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ 1338c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), \ 1348c2ecf20Sopenharmony_ci .channels_min = 1, \ 1358c2ecf20Sopenharmony_ci .channels_max = 8, \ 1368c2ecf20Sopenharmony_ci .rate_min = 8000, \ 1378c2ecf20Sopenharmony_ci .rate_max = 192000, \ 1388c2ecf20Sopenharmony_ci }, \ 1398c2ecf20Sopenharmony_ci .capture = { \ 1408c2ecf20Sopenharmony_ci .stream_name = "MultiMedia"#num" Capture", \ 1418c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_8000_48000| \ 1428c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_KNOT), \ 1438c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ 1448c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE), \ 1458c2ecf20Sopenharmony_ci .channels_min = 1, \ 1468c2ecf20Sopenharmony_ci .channels_max = 4, \ 1478c2ecf20Sopenharmony_ci .rate_min = 8000, \ 1488c2ecf20Sopenharmony_ci .rate_max = 48000, \ 1498c2ecf20Sopenharmony_ci }, \ 1508c2ecf20Sopenharmony_ci .name = "MultiMedia"#num, \ 1518c2ecf20Sopenharmony_ci .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \ 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* Conventional and unconventional sample rate supported */ 1558c2ecf20Sopenharmony_cistatic unsigned int supported_sample_rates[] = { 1568c2ecf20Sopenharmony_ci 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 1578c2ecf20Sopenharmony_ci 88200, 96000, 176400, 192000 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct snd_pcm_hw_constraint_list constraints_sample_rates = { 1618c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(supported_sample_rates), 1628c2ecf20Sopenharmony_ci .list = supported_sample_rates, 1638c2ecf20Sopenharmony_ci .mask = 0, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct snd_compr_codec_caps q6asm_compr_caps = { 1678c2ecf20Sopenharmony_ci .num_descriptors = 1, 1688c2ecf20Sopenharmony_ci .descriptor[0].max_ch = 2, 1698c2ecf20Sopenharmony_ci .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, 1708c2ecf20Sopenharmony_ci 24000, 32000, 44100, 48000, 88200, 1718c2ecf20Sopenharmony_ci 96000, 176400, 192000 }, 1728c2ecf20Sopenharmony_ci .descriptor[0].num_sample_rates = 13, 1738c2ecf20Sopenharmony_ci .descriptor[0].bit_rate[0] = 320, 1748c2ecf20Sopenharmony_ci .descriptor[0].bit_rate[1] = 128, 1758c2ecf20Sopenharmony_ci .descriptor[0].num_bitrates = 2, 1768c2ecf20Sopenharmony_ci .descriptor[0].profiles = 0, 1778c2ecf20Sopenharmony_ci .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, 1788c2ecf20Sopenharmony_ci .descriptor[0].formats = 0, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void event_handler(uint32_t opcode, uint32_t token, 1828c2ecf20Sopenharmony_ci void *payload, void *priv) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = priv; 1858c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = prtd->substream; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci switch (opcode) { 1888c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_RUN_DONE: 1898c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1908c2ecf20Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 1918c2ecf20Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_EOS_DONE: 1948c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { 1978c2ecf20Sopenharmony_ci prtd->pcm_irq_pos += prtd->pcm_count; 1988c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 1998c2ecf20Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING) 2008c2ecf20Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 2018c2ecf20Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_READ_DONE: 2068c2ecf20Sopenharmony_ci prtd->pcm_irq_pos += prtd->pcm_count; 2078c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 2088c2ecf20Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING) 2098c2ecf20Sopenharmony_ci q6asm_read(prtd->audio_client, prtd->stream_id); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci default: 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int q6asm_dai_prepare(struct snd_soc_component *component, 2188c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); 2228c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 2238c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata; 2248c2ecf20Sopenharmony_ci struct device *dev = component->dev; 2258c2ecf20Sopenharmony_ci int ret, i; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 2288c2ecf20Sopenharmony_ci if (!pdata) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!prtd || !prtd->audio_client) { 2328c2ecf20Sopenharmony_ci dev_err(dev, "%s: private data null or audio client freed\n", 2338c2ecf20Sopenharmony_ci __func__); 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci prtd->pcm_count = snd_pcm_lib_period_bytes(substream); 2388c2ecf20Sopenharmony_ci prtd->pcm_irq_pos = 0; 2398c2ecf20Sopenharmony_ci /* rate and channels are sent to audio driver */ 2408c2ecf20Sopenharmony_ci if (prtd->state) { 2418c2ecf20Sopenharmony_ci /* clear the previous setup if any */ 2428c2ecf20Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); 2438c2ecf20Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, 2448c2ecf20Sopenharmony_ci prtd->audio_client); 2458c2ecf20Sopenharmony_ci q6routing_stream_close(soc_prtd->dai_link->id, 2468c2ecf20Sopenharmony_ci substream->stream); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, 2508c2ecf20Sopenharmony_ci prtd->phys, 2518c2ecf20Sopenharmony_ci (prtd->pcm_size / prtd->periods), 2528c2ecf20Sopenharmony_ci prtd->periods); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (ret < 0) { 2558c2ecf20Sopenharmony_ci dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", 2568c2ecf20Sopenharmony_ci ret); 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2618c2ecf20Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, 2628c2ecf20Sopenharmony_ci FORMAT_LINEAR_PCM, 2638c2ecf20Sopenharmony_ci 0, prtd->bits_per_sample, false); 2648c2ecf20Sopenharmony_ci } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 2658c2ecf20Sopenharmony_ci ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, 2668c2ecf20Sopenharmony_ci FORMAT_LINEAR_PCM, 2678c2ecf20Sopenharmony_ci prtd->bits_per_sample); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (ret < 0) { 2718c2ecf20Sopenharmony_ci dev_err(dev, "%s: q6asm_open_write failed\n", __func__); 2728c2ecf20Sopenharmony_ci goto open_err; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci prtd->session_id = q6asm_get_session_id(prtd->audio_client); 2768c2ecf20Sopenharmony_ci ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, 2778c2ecf20Sopenharmony_ci prtd->session_id, substream->stream); 2788c2ecf20Sopenharmony_ci if (ret) { 2798c2ecf20Sopenharmony_ci dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret); 2808c2ecf20Sopenharmony_ci goto routing_err; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2848c2ecf20Sopenharmony_ci ret = q6asm_media_format_block_multi_ch_pcm( 2858c2ecf20Sopenharmony_ci prtd->audio_client, prtd->stream_id, 2868c2ecf20Sopenharmony_ci runtime->rate, runtime->channels, NULL, 2878c2ecf20Sopenharmony_ci prtd->bits_per_sample); 2888c2ecf20Sopenharmony_ci } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 2898c2ecf20Sopenharmony_ci ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, 2908c2ecf20Sopenharmony_ci prtd->stream_id, 2918c2ecf20Sopenharmony_ci runtime->rate, 2928c2ecf20Sopenharmony_ci runtime->channels, 2938c2ecf20Sopenharmony_ci prtd->bits_per_sample); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Queue the buffers */ 2968c2ecf20Sopenharmony_ci for (i = 0; i < runtime->periods; i++) 2978c2ecf20Sopenharmony_ci q6asm_read(prtd->audio_client, prtd->stream_id); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci if (ret < 0) 3018c2ecf20Sopenharmony_ci dev_info(dev, "%s: CMD Format block failed\n", __func__); 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_RUNNING; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cirouting_err: 3088c2ecf20Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); 3098c2ecf20Sopenharmony_ciopen_err: 3108c2ecf20Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); 3118c2ecf20Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 3128c2ecf20Sopenharmony_ci prtd->audio_client = NULL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int q6asm_dai_trigger(struct snd_soc_component *component, 3188c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int ret = 0; 3218c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3228c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci switch (cmd) { 3258c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3268c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3278c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3288c2ecf20Sopenharmony_ci ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, 3298c2ecf20Sopenharmony_ci 0, 0, 0); 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3328c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 3338c2ecf20Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 3348c2ecf20Sopenharmony_ci CMD_EOS); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3378c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3388c2ecf20Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 3398c2ecf20Sopenharmony_ci CMD_PAUSE); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci default: 3428c2ecf20Sopenharmony_ci ret = -EINVAL; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int q6asm_dai_open(struct snd_soc_component *component, 3508c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3538c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); 3548c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0); 3558c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd; 3568c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata; 3578c2ecf20Sopenharmony_ci struct device *dev = component->dev; 3588c2ecf20Sopenharmony_ci int ret = 0; 3598c2ecf20Sopenharmony_ci int stream_id; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci stream_id = cpu_dai->driver->id; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 3648c2ecf20Sopenharmony_ci if (!pdata) { 3658c2ecf20Sopenharmony_ci dev_err(dev, "Drv data not found ..\n"); 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL); 3708c2ecf20Sopenharmony_ci if (prtd == NULL) 3718c2ecf20Sopenharmony_ci return -ENOMEM; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci prtd->substream = substream; 3748c2ecf20Sopenharmony_ci prtd->audio_client = q6asm_audio_client_alloc(dev, 3758c2ecf20Sopenharmony_ci (q6asm_cb)event_handler, prtd, stream_id, 3768c2ecf20Sopenharmony_ci LEGACY_PCM_MODE); 3778c2ecf20Sopenharmony_ci if (IS_ERR(prtd->audio_client)) { 3788c2ecf20Sopenharmony_ci dev_info(dev, "%s: Could not allocate memory\n", __func__); 3798c2ecf20Sopenharmony_ci ret = PTR_ERR(prtd->audio_client); 3808c2ecf20Sopenharmony_ci kfree(prtd); 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* DSP expects stream id from 1 */ 3858c2ecf20Sopenharmony_ci prtd->stream_id = 1; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3888c2ecf20Sopenharmony_ci runtime->hw = q6asm_dai_hardware_playback; 3898c2ecf20Sopenharmony_ci else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 3908c2ecf20Sopenharmony_ci runtime->hw = q6asm_dai_hardware_capture; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_list(runtime, 0, 3938c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 3948c2ecf20Sopenharmony_ci &constraints_sample_rates); 3958c2ecf20Sopenharmony_ci if (ret < 0) 3968c2ecf20Sopenharmony_ci dev_info(dev, "snd_pcm_hw_constraint_list failed\n"); 3978c2ecf20Sopenharmony_ci /* Ensure that buffer size is a multiple of period size */ 3988c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, 3998c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 4008c2ecf20Sopenharmony_ci if (ret < 0) 4018c2ecf20Sopenharmony_ci dev_info(dev, "snd_pcm_hw_constraint_integer failed\n"); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 4048c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(runtime, 4058c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4068c2ecf20Sopenharmony_ci PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, 4078c2ecf20Sopenharmony_ci PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); 4088c2ecf20Sopenharmony_ci if (ret < 0) { 4098c2ecf20Sopenharmony_ci dev_err(dev, "constraint for buffer bytes min max ret = %d\n", 4108c2ecf20Sopenharmony_ci ret); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 4158c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 4168c2ecf20Sopenharmony_ci if (ret < 0) { 4178c2ecf20Sopenharmony_ci dev_err(dev, "constraint for period bytes step ret = %d\n", 4188c2ecf20Sopenharmony_ci ret); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 4218c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 4228c2ecf20Sopenharmony_ci if (ret < 0) { 4238c2ecf20Sopenharmony_ci dev_err(dev, "constraint for buffer bytes step ret = %d\n", 4248c2ecf20Sopenharmony_ci ret); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci runtime->private_data = prtd; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (pdata->sid < 0) 4358c2ecf20Sopenharmony_ci prtd->phys = substream->dma_buffer.addr; 4368c2ecf20Sopenharmony_ci else 4378c2ecf20Sopenharmony_ci prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int q6asm_dai_close(struct snd_soc_component *component, 4458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4488c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); 4498c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (prtd->audio_client) { 4528c2ecf20Sopenharmony_ci if (prtd->state) 4538c2ecf20Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, 4548c2ecf20Sopenharmony_ci CMD_CLOSE); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci q6asm_unmap_memory_regions(substream->stream, 4578c2ecf20Sopenharmony_ci prtd->audio_client); 4588c2ecf20Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 4598c2ecf20Sopenharmony_ci prtd->audio_client = NULL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci q6routing_stream_close(soc_prtd->dai_link->id, 4628c2ecf20Sopenharmony_ci substream->stream); 4638c2ecf20Sopenharmony_ci kfree(prtd); 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component, 4688c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4728c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (prtd->pcm_irq_pos >= prtd->pcm_size) 4758c2ecf20Sopenharmony_ci prtd->pcm_irq_pos = 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int q6asm_dai_mmap(struct snd_soc_component *component, 4818c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 4828c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4858c2ecf20Sopenharmony_ci struct device *dev = component->dev; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return dma_mmap_coherent(dev, vma, 4888c2ecf20Sopenharmony_ci runtime->dma_area, runtime->dma_addr, 4898c2ecf20Sopenharmony_ci runtime->dma_bytes); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int q6asm_dai_hw_params(struct snd_soc_component *component, 4938c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 4948c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4978c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci prtd->pcm_size = params_buffer_bytes(params); 5008c2ecf20Sopenharmony_ci prtd->periods = params_periods(params); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci switch (params_format(params)) { 5038c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 5048c2ecf20Sopenharmony_ci prtd->bits_per_sample = 16; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 5078c2ecf20Sopenharmony_ci prtd->bits_per_sample = 24; 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void compress_event_handler(uint32_t opcode, uint32_t token, 5158c2ecf20Sopenharmony_ci void *payload, void *priv) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = priv; 5188c2ecf20Sopenharmony_ci struct snd_compr_stream *substream = prtd->cstream; 5198c2ecf20Sopenharmony_ci unsigned long flags; 5208c2ecf20Sopenharmony_ci u32 wflags = 0; 5218c2ecf20Sopenharmony_ci uint64_t avail; 5228c2ecf20Sopenharmony_ci uint32_t bytes_written, bytes_to_write; 5238c2ecf20Sopenharmony_ci bool is_last_buffer = false; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci switch (opcode) { 5268c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_RUN_DONE: 5278c2ecf20Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 5288c2ecf20Sopenharmony_ci if (!prtd->bytes_sent) { 5298c2ecf20Sopenharmony_ci q6asm_stream_remove_initial_silence(prtd->audio_client, 5308c2ecf20Sopenharmony_ci prtd->stream_id, 5318c2ecf20Sopenharmony_ci prtd->initial_samples_drop); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 5348c2ecf20Sopenharmony_ci prtd->pcm_count, 0, 0, 0); 5358c2ecf20Sopenharmony_ci prtd->bytes_sent += prtd->pcm_count; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_CMD_EOS_DONE: 5428c2ecf20Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 5438c2ecf20Sopenharmony_ci if (prtd->notify_on_drain) { 5448c2ecf20Sopenharmony_ci if (substream->partial_drain) { 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * Close old stream and make it stale, switch 5478c2ecf20Sopenharmony_ci * the active stream now! 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci q6asm_cmd_nowait(prtd->audio_client, 5508c2ecf20Sopenharmony_ci prtd->stream_id, 5518c2ecf20Sopenharmony_ci CMD_CLOSE); 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * vaild stream ids start from 1, So we are 5548c2ecf20Sopenharmony_ci * toggling this between 1 and 2. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci snd_compr_drain_notify(prtd->cstream); 5608c2ecf20Sopenharmony_ci prtd->notify_on_drain = false; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci } else { 5638c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci case ASM_CLIENT_EVENT_DATA_WRITE_DONE: 5698c2ecf20Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; 5728c2ecf20Sopenharmony_ci prtd->copied_total += bytes_written; 5738c2ecf20Sopenharmony_ci snd_compr_fragment_elapsed(substream); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (prtd->state != Q6ASM_STREAM_RUNNING) { 5768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 5818c2ecf20Sopenharmony_ci if (avail > prtd->pcm_count) { 5828c2ecf20Sopenharmony_ci bytes_to_write = prtd->pcm_count; 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci if (substream->partial_drain || prtd->notify_on_drain) 5858c2ecf20Sopenharmony_ci is_last_buffer = true; 5868c2ecf20Sopenharmony_ci bytes_to_write = avail; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (bytes_to_write) { 5908c2ecf20Sopenharmony_ci if (substream->partial_drain && is_last_buffer) { 5918c2ecf20Sopenharmony_ci wflags |= ASM_LAST_BUFFER_FLAG; 5928c2ecf20Sopenharmony_ci q6asm_stream_remove_trailing_silence(prtd->audio_client, 5938c2ecf20Sopenharmony_ci prtd->stream_id, 5948c2ecf20Sopenharmony_ci prtd->trailing_samples_drop); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 5988c2ecf20Sopenharmony_ci bytes_to_write, 0, 0, wflags); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (prtd->notify_on_drain && is_last_buffer) 6048c2ecf20Sopenharmony_ci q6asm_cmd_nowait(prtd->audio_client, 6058c2ecf20Sopenharmony_ci prtd->stream_id, CMD_EOS); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci default: 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_open(struct snd_soc_component *component, 6168c2ecf20Sopenharmony_ci struct snd_compr_stream *stream) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 6198c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 6208c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 6218c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata; 6228c2ecf20Sopenharmony_ci struct device *dev = component->dev; 6238c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd; 6248c2ecf20Sopenharmony_ci int stream_id, size, ret; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci stream_id = cpu_dai->driver->id; 6278c2ecf20Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 6288c2ecf20Sopenharmony_ci if (!pdata) { 6298c2ecf20Sopenharmony_ci dev_err(dev, "Drv data not found ..\n"); 6308c2ecf20Sopenharmony_ci return -EINVAL; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 6348c2ecf20Sopenharmony_ci if (!prtd) 6358c2ecf20Sopenharmony_ci return -ENOMEM; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* DSP expects stream id from 1 */ 6388c2ecf20Sopenharmony_ci prtd->stream_id = 1; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci prtd->cstream = stream; 6418c2ecf20Sopenharmony_ci prtd->audio_client = q6asm_audio_client_alloc(dev, 6428c2ecf20Sopenharmony_ci (q6asm_cb)compress_event_handler, 6438c2ecf20Sopenharmony_ci prtd, stream_id, LEGACY_PCM_MODE); 6448c2ecf20Sopenharmony_ci if (IS_ERR(prtd->audio_client)) { 6458c2ecf20Sopenharmony_ci dev_err(dev, "Could not allocate memory\n"); 6468c2ecf20Sopenharmony_ci ret = PTR_ERR(prtd->audio_client); 6478c2ecf20Sopenharmony_ci goto free_prtd; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * 6518c2ecf20Sopenharmony_ci COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 6528c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, 6538c2ecf20Sopenharmony_ci &prtd->dma_buffer); 6548c2ecf20Sopenharmony_ci if (ret) { 6558c2ecf20Sopenharmony_ci dev_err(dev, "Cannot allocate buffer(s)\n"); 6568c2ecf20Sopenharmony_ci goto free_client; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (pdata->sid < 0) 6608c2ecf20Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr; 6618c2ecf20Sopenharmony_ci else 6628c2ecf20Sopenharmony_ci prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); 6658c2ecf20Sopenharmony_ci spin_lock_init(&prtd->lock); 6668c2ecf20Sopenharmony_ci runtime->private_data = prtd; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cifree_client: 6718c2ecf20Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 6728c2ecf20Sopenharmony_cifree_prtd: 6738c2ecf20Sopenharmony_ci kfree(prtd); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return ret; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_free(struct snd_soc_component *component, 6798c2ecf20Sopenharmony_ci struct snd_compr_stream *stream) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 6828c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 6838c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (prtd->audio_client) { 6868c2ecf20Sopenharmony_ci if (prtd->state) { 6878c2ecf20Sopenharmony_ci q6asm_cmd(prtd->audio_client, prtd->stream_id, 6888c2ecf20Sopenharmony_ci CMD_CLOSE); 6898c2ecf20Sopenharmony_ci if (prtd->next_track_stream_id) { 6908c2ecf20Sopenharmony_ci q6asm_cmd(prtd->audio_client, 6918c2ecf20Sopenharmony_ci prtd->next_track_stream_id, 6928c2ecf20Sopenharmony_ci CMD_CLOSE); 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci snd_dma_free_pages(&prtd->dma_buffer); 6978c2ecf20Sopenharmony_ci q6asm_unmap_memory_regions(stream->direction, 6988c2ecf20Sopenharmony_ci prtd->audio_client); 6998c2ecf20Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 7008c2ecf20Sopenharmony_ci prtd->audio_client = NULL; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci q6routing_stream_close(rtd->dai_link->id, stream->direction); 7038c2ecf20Sopenharmony_ci kfree(prtd); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, 7098c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 7108c2ecf20Sopenharmony_ci struct snd_codec *codec, 7118c2ecf20Sopenharmony_ci int stream_id) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 7148c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 7158c2ecf20Sopenharmony_ci struct q6asm_flac_cfg flac_cfg; 7168c2ecf20Sopenharmony_ci struct q6asm_wma_cfg wma_cfg; 7178c2ecf20Sopenharmony_ci struct q6asm_alac_cfg alac_cfg; 7188c2ecf20Sopenharmony_ci struct q6asm_ape_cfg ape_cfg; 7198c2ecf20Sopenharmony_ci unsigned int wma_v9 = 0; 7208c2ecf20Sopenharmony_ci struct device *dev = component->dev; 7218c2ecf20Sopenharmony_ci int ret; 7228c2ecf20Sopenharmony_ci union snd_codec_options *codec_options; 7238c2ecf20Sopenharmony_ci struct snd_dec_flac *flac; 7248c2ecf20Sopenharmony_ci struct snd_dec_wma *wma; 7258c2ecf20Sopenharmony_ci struct snd_dec_alac *alac; 7268c2ecf20Sopenharmony_ci struct snd_dec_ape *ape; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci codec_options = &(prtd->codec.options); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci memcpy(&prtd->codec, codec, sizeof(*codec)); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci switch (codec->id) { 7338c2ecf20Sopenharmony_ci case SND_AUDIOCODEC_FLAC: 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); 7368c2ecf20Sopenharmony_ci flac = &codec_options->flac_d; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci flac_cfg.ch_cfg = codec->ch_in; 7398c2ecf20Sopenharmony_ci flac_cfg.sample_rate = codec->sample_rate; 7408c2ecf20Sopenharmony_ci flac_cfg.stream_info_present = 1; 7418c2ecf20Sopenharmony_ci flac_cfg.sample_size = flac->sample_size; 7428c2ecf20Sopenharmony_ci flac_cfg.min_blk_size = flac->min_blk_size; 7438c2ecf20Sopenharmony_ci flac_cfg.max_blk_size = flac->max_blk_size; 7448c2ecf20Sopenharmony_ci flac_cfg.max_frame_size = flac->max_frame_size; 7458c2ecf20Sopenharmony_ci flac_cfg.min_frame_size = flac->min_frame_size; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = q6asm_stream_media_format_block_flac(prtd->audio_client, 7488c2ecf20Sopenharmony_ci stream_id, 7498c2ecf20Sopenharmony_ci &flac_cfg); 7508c2ecf20Sopenharmony_ci if (ret < 0) { 7518c2ecf20Sopenharmony_ci dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); 7528c2ecf20Sopenharmony_ci return -EIO; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci break; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci case SND_AUDIOCODEC_WMA: 7578c2ecf20Sopenharmony_ci wma = &codec_options->wma_d; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci wma_cfg.sample_rate = codec->sample_rate; 7628c2ecf20Sopenharmony_ci wma_cfg.num_channels = codec->ch_in; 7638c2ecf20Sopenharmony_ci wma_cfg.bytes_per_sec = codec->bit_rate / 8; 7648c2ecf20Sopenharmony_ci wma_cfg.block_align = codec->align; 7658c2ecf20Sopenharmony_ci wma_cfg.bits_per_sample = prtd->bits_per_sample; 7668c2ecf20Sopenharmony_ci wma_cfg.enc_options = wma->encoder_option; 7678c2ecf20Sopenharmony_ci wma_cfg.adv_enc_options = wma->adv_encoder_option; 7688c2ecf20Sopenharmony_ci wma_cfg.adv_enc_options2 = wma->adv_encoder_option2; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (wma_cfg.num_channels == 1) 7718c2ecf20Sopenharmony_ci wma_cfg.channel_mask = 4; /* Mono Center */ 7728c2ecf20Sopenharmony_ci else if (wma_cfg.num_channels == 2) 7738c2ecf20Sopenharmony_ci wma_cfg.channel_mask = 3; /* Stereo FL/FR */ 7748c2ecf20Sopenharmony_ci else 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* check the codec profile */ 7788c2ecf20Sopenharmony_ci switch (codec->profile) { 7798c2ecf20Sopenharmony_ci case SND_AUDIOPROFILE_WMA9: 7808c2ecf20Sopenharmony_ci wma_cfg.fmtag = 0x161; 7818c2ecf20Sopenharmony_ci wma_v9 = 1; 7828c2ecf20Sopenharmony_ci break; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci case SND_AUDIOPROFILE_WMA10: 7858c2ecf20Sopenharmony_ci wma_cfg.fmtag = 0x166; 7868c2ecf20Sopenharmony_ci break; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_PRO: 7898c2ecf20Sopenharmony_ci wma_cfg.fmtag = 0x162; 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci case SND_AUDIOPROFILE_WMA9_LOSSLESS: 7938c2ecf20Sopenharmony_ci wma_cfg.fmtag = 0x163; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci case SND_AUDIOPROFILE_WMA10_LOSSLESS: 7978c2ecf20Sopenharmony_ci wma_cfg.fmtag = 0x167; 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci default: 8018c2ecf20Sopenharmony_ci dev_err(dev, "Unknown WMA profile:%x\n", 8028c2ecf20Sopenharmony_ci codec->profile); 8038c2ecf20Sopenharmony_ci return -EIO; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (wma_v9) 8078c2ecf20Sopenharmony_ci ret = q6asm_stream_media_format_block_wma_v9( 8088c2ecf20Sopenharmony_ci prtd->audio_client, stream_id, 8098c2ecf20Sopenharmony_ci &wma_cfg); 8108c2ecf20Sopenharmony_ci else 8118c2ecf20Sopenharmony_ci ret = q6asm_stream_media_format_block_wma_v10( 8128c2ecf20Sopenharmony_ci prtd->audio_client, stream_id, 8138c2ecf20Sopenharmony_ci &wma_cfg); 8148c2ecf20Sopenharmony_ci if (ret < 0) { 8158c2ecf20Sopenharmony_ci dev_err(dev, "WMA9 CMD failed:%d\n", ret); 8168c2ecf20Sopenharmony_ci return -EIO; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci case SND_AUDIOCODEC_ALAC: 8218c2ecf20Sopenharmony_ci memset(&alac_cfg, 0x0, sizeof(alac_cfg)); 8228c2ecf20Sopenharmony_ci alac = &codec_options->alac_d; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci alac_cfg.sample_rate = codec->sample_rate; 8258c2ecf20Sopenharmony_ci alac_cfg.avg_bit_rate = codec->bit_rate; 8268c2ecf20Sopenharmony_ci alac_cfg.bit_depth = prtd->bits_per_sample; 8278c2ecf20Sopenharmony_ci alac_cfg.num_channels = codec->ch_in; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci alac_cfg.frame_length = alac->frame_length; 8308c2ecf20Sopenharmony_ci alac_cfg.pb = alac->pb; 8318c2ecf20Sopenharmony_ci alac_cfg.mb = alac->mb; 8328c2ecf20Sopenharmony_ci alac_cfg.kb = alac->kb; 8338c2ecf20Sopenharmony_ci alac_cfg.max_run = alac->max_run; 8348c2ecf20Sopenharmony_ci alac_cfg.compatible_version = alac->compatible_version; 8358c2ecf20Sopenharmony_ci alac_cfg.max_frame_bytes = alac->max_frame_bytes; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci switch (codec->ch_in) { 8388c2ecf20Sopenharmony_ci case 1: 8398c2ecf20Sopenharmony_ci alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci case 2: 8428c2ecf20Sopenharmony_ci alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO; 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci ret = q6asm_stream_media_format_block_alac(prtd->audio_client, 8468c2ecf20Sopenharmony_ci stream_id, 8478c2ecf20Sopenharmony_ci &alac_cfg); 8488c2ecf20Sopenharmony_ci if (ret < 0) { 8498c2ecf20Sopenharmony_ci dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); 8508c2ecf20Sopenharmony_ci return -EIO; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci case SND_AUDIOCODEC_APE: 8558c2ecf20Sopenharmony_ci memset(&ape_cfg, 0x0, sizeof(ape_cfg)); 8568c2ecf20Sopenharmony_ci ape = &codec_options->ape_d; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci ape_cfg.sample_rate = codec->sample_rate; 8598c2ecf20Sopenharmony_ci ape_cfg.num_channels = codec->ch_in; 8608c2ecf20Sopenharmony_ci ape_cfg.bits_per_sample = prtd->bits_per_sample; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci ape_cfg.compatible_version = ape->compatible_version; 8638c2ecf20Sopenharmony_ci ape_cfg.compression_level = ape->compression_level; 8648c2ecf20Sopenharmony_ci ape_cfg.format_flags = ape->format_flags; 8658c2ecf20Sopenharmony_ci ape_cfg.blocks_per_frame = ape->blocks_per_frame; 8668c2ecf20Sopenharmony_ci ape_cfg.final_frame_blocks = ape->final_frame_blocks; 8678c2ecf20Sopenharmony_ci ape_cfg.total_frames = ape->total_frames; 8688c2ecf20Sopenharmony_ci ape_cfg.seek_table_present = ape->seek_table_present; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = q6asm_stream_media_format_block_ape(prtd->audio_client, 8718c2ecf20Sopenharmony_ci stream_id, 8728c2ecf20Sopenharmony_ci &ape_cfg); 8738c2ecf20Sopenharmony_ci if (ret < 0) { 8748c2ecf20Sopenharmony_ci dev_err(dev, "APE CMD Format block failed:%d\n", ret); 8758c2ecf20Sopenharmony_ci return -EIO; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci default: 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_set_params(struct snd_soc_component *component, 8878c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 8888c2ecf20Sopenharmony_ci struct snd_compr_params *params) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 8918c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 8928c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 8938c2ecf20Sopenharmony_ci int dir = stream->direction; 8948c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata; 8958c2ecf20Sopenharmony_ci struct device *dev = component->dev; 8968c2ecf20Sopenharmony_ci int ret; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci pdata = snd_soc_component_get_drvdata(component); 8998c2ecf20Sopenharmony_ci if (!pdata) 9008c2ecf20Sopenharmony_ci return -EINVAL; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!prtd || !prtd->audio_client) { 9038c2ecf20Sopenharmony_ci dev_err(dev, "private data null or audio client freed\n"); 9048c2ecf20Sopenharmony_ci return -EINVAL; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci prtd->periods = runtime->fragments; 9088c2ecf20Sopenharmony_ci prtd->pcm_count = runtime->fragment_size; 9098c2ecf20Sopenharmony_ci prtd->pcm_size = runtime->fragments * runtime->fragment_size; 9108c2ecf20Sopenharmony_ci prtd->bits_per_sample = 16; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (dir == SND_COMPRESS_PLAYBACK) { 9138c2ecf20Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, 9148c2ecf20Sopenharmony_ci params->codec.profile, prtd->bits_per_sample, 9158c2ecf20Sopenharmony_ci true); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (ret < 0) { 9188c2ecf20Sopenharmony_ci dev_err(dev, "q6asm_open_write failed\n"); 9198c2ecf20Sopenharmony_ci q6asm_audio_client_free(prtd->audio_client); 9208c2ecf20Sopenharmony_ci prtd->audio_client = NULL; 9218c2ecf20Sopenharmony_ci return ret; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci prtd->session_id = q6asm_get_session_id(prtd->audio_client); 9268c2ecf20Sopenharmony_ci ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, 9278c2ecf20Sopenharmony_ci prtd->session_id, dir); 9288c2ecf20Sopenharmony_ci if (ret) { 9298c2ecf20Sopenharmony_ci dev_err(dev, "Stream reg failed ret:%d\n", ret); 9308c2ecf20Sopenharmony_ci return ret; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci ret = __q6asm_dai_compr_set_codec_params(component, stream, 9348c2ecf20Sopenharmony_ci ¶ms->codec, 9358c2ecf20Sopenharmony_ci prtd->stream_id); 9368c2ecf20Sopenharmony_ci if (ret) { 9378c2ecf20Sopenharmony_ci dev_err(dev, "codec param setup failed ret:%d\n", ret); 9388c2ecf20Sopenharmony_ci return ret; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, 9428c2ecf20Sopenharmony_ci (prtd->pcm_size / prtd->periods), 9438c2ecf20Sopenharmony_ci prtd->periods); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (ret < 0) { 9468c2ecf20Sopenharmony_ci dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); 9478c2ecf20Sopenharmony_ci return -ENOMEM; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_RUNNING; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, 9568c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 9578c2ecf20Sopenharmony_ci struct snd_compr_metadata *metadata) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 9608c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 9618c2ecf20Sopenharmony_ci int ret = 0; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci switch (metadata->key) { 9648c2ecf20Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_PADDING: 9658c2ecf20Sopenharmony_ci prtd->trailing_samples_drop = metadata->value[0]; 9668c2ecf20Sopenharmony_ci break; 9678c2ecf20Sopenharmony_ci case SNDRV_COMPRESS_ENCODER_DELAY: 9688c2ecf20Sopenharmony_ci prtd->initial_samples_drop = metadata->value[0]; 9698c2ecf20Sopenharmony_ci if (prtd->next_track_stream_id) { 9708c2ecf20Sopenharmony_ci ret = q6asm_open_write(prtd->audio_client, 9718c2ecf20Sopenharmony_ci prtd->next_track_stream_id, 9728c2ecf20Sopenharmony_ci prtd->codec.id, 9738c2ecf20Sopenharmony_ci prtd->codec.profile, 9748c2ecf20Sopenharmony_ci prtd->bits_per_sample, 9758c2ecf20Sopenharmony_ci true); 9768c2ecf20Sopenharmony_ci if (ret < 0) { 9778c2ecf20Sopenharmony_ci dev_err(component->dev, "q6asm_open_write failed\n"); 9788c2ecf20Sopenharmony_ci return ret; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci ret = __q6asm_dai_compr_set_codec_params(component, stream, 9818c2ecf20Sopenharmony_ci &prtd->codec, 9828c2ecf20Sopenharmony_ci prtd->next_track_stream_id); 9838c2ecf20Sopenharmony_ci if (ret < 0) { 9848c2ecf20Sopenharmony_ci dev_err(component->dev, "q6asm_open_write failed\n"); 9858c2ecf20Sopenharmony_ci return ret; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci ret = q6asm_stream_remove_initial_silence(prtd->audio_client, 9898c2ecf20Sopenharmony_ci prtd->next_track_stream_id, 9908c2ecf20Sopenharmony_ci prtd->initial_samples_drop); 9918c2ecf20Sopenharmony_ci prtd->next_track_stream_id = 0; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci break; 9968c2ecf20Sopenharmony_ci default: 9978c2ecf20Sopenharmony_ci ret = -EINVAL; 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return ret; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_trigger(struct snd_soc_component *component, 10058c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, int cmd) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 10088c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 10098c2ecf20Sopenharmony_ci int ret = 0; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci switch (cmd) { 10128c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 10138c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 10148c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 10158c2ecf20Sopenharmony_ci ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, 10168c2ecf20Sopenharmony_ci 0, 0, 0); 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 10198c2ecf20Sopenharmony_ci prtd->state = Q6ASM_STREAM_STOPPED; 10208c2ecf20Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 10218c2ecf20Sopenharmony_ci CMD_EOS); 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 10248c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 10258c2ecf20Sopenharmony_ci ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, 10268c2ecf20Sopenharmony_ci CMD_PAUSE); 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci case SND_COMPR_TRIGGER_NEXT_TRACK: 10298c2ecf20Sopenharmony_ci prtd->next_track = true; 10308c2ecf20Sopenharmony_ci prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci case SND_COMPR_TRIGGER_DRAIN: 10338c2ecf20Sopenharmony_ci case SND_COMPR_TRIGGER_PARTIAL_DRAIN: 10348c2ecf20Sopenharmony_ci prtd->notify_on_drain = true; 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci default: 10378c2ecf20Sopenharmony_ci ret = -EINVAL; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci return ret; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_pointer(struct snd_soc_component *component, 10458c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 10468c2ecf20Sopenharmony_ci struct snd_compr_tstamp *tstamp) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 10498c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 10508c2ecf20Sopenharmony_ci unsigned long flags; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci tstamp->copied_total = prtd->copied_total; 10558c2ecf20Sopenharmony_ci tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 0; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic int q6asm_compr_copy(struct snd_soc_component *component, 10638c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, char __user *buf, 10648c2ecf20Sopenharmony_ci size_t count) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 10678c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 10688c2ecf20Sopenharmony_ci unsigned long flags; 10698c2ecf20Sopenharmony_ci u32 wflags = 0; 10708c2ecf20Sopenharmony_ci int avail, bytes_in_flight = 0; 10718c2ecf20Sopenharmony_ci void *dstn; 10728c2ecf20Sopenharmony_ci size_t copy; 10738c2ecf20Sopenharmony_ci u32 app_pointer; 10748c2ecf20Sopenharmony_ci u32 bytes_received; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci bytes_received = prtd->bytes_received; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /** 10798c2ecf20Sopenharmony_ci * Make sure that next track data pointer is aligned at 32 bit boundary 10808c2ecf20Sopenharmony_ci * This is a Mandatory requirement from DSP data buffers alignment 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci if (prtd->next_track) 10838c2ecf20Sopenharmony_ci bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci app_pointer = bytes_received/prtd->pcm_size; 10868c2ecf20Sopenharmony_ci app_pointer = bytes_received - (app_pointer * prtd->pcm_size); 10878c2ecf20Sopenharmony_ci dstn = prtd->dma_buffer.area + app_pointer; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (count < prtd->pcm_size - app_pointer) { 10908c2ecf20Sopenharmony_ci if (copy_from_user(dstn, buf, count)) 10918c2ecf20Sopenharmony_ci return -EFAULT; 10928c2ecf20Sopenharmony_ci } else { 10938c2ecf20Sopenharmony_ci copy = prtd->pcm_size - app_pointer; 10948c2ecf20Sopenharmony_ci if (copy_from_user(dstn, buf, copy)) 10958c2ecf20Sopenharmony_ci return -EFAULT; 10968c2ecf20Sopenharmony_ci if (copy_from_user(prtd->dma_buffer.area, buf + copy, 10978c2ecf20Sopenharmony_ci count - copy)) 10988c2ecf20Sopenharmony_ci return -EFAULT; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci spin_lock_irqsave(&prtd->lock, flags); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci bytes_in_flight = prtd->bytes_received - prtd->copied_total; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (prtd->next_track) { 11068c2ecf20Sopenharmony_ci prtd->next_track = false; 11078c2ecf20Sopenharmony_ci prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); 11088c2ecf20Sopenharmony_ci prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci prtd->bytes_received = bytes_received + count; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* Kick off the data to dsp if its starving!! */ 11148c2ecf20Sopenharmony_ci if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { 11158c2ecf20Sopenharmony_ci uint32_t bytes_to_write = prtd->pcm_count; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci avail = prtd->bytes_received - prtd->bytes_sent; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (avail < prtd->pcm_count) 11208c2ecf20Sopenharmony_ci bytes_to_write = avail; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci q6asm_write_async(prtd->audio_client, prtd->stream_id, 11238c2ecf20Sopenharmony_ci bytes_to_write, 0, 0, wflags); 11248c2ecf20Sopenharmony_ci prtd->bytes_sent += bytes_to_write; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&prtd->lock, flags); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return count; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_mmap(struct snd_soc_component *component, 11338c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 11348c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 11378c2ecf20Sopenharmony_ci struct q6asm_dai_rtd *prtd = runtime->private_data; 11388c2ecf20Sopenharmony_ci struct device *dev = component->dev; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return dma_mmap_coherent(dev, vma, 11418c2ecf20Sopenharmony_ci prtd->dma_buffer.area, prtd->dma_buffer.addr, 11428c2ecf20Sopenharmony_ci prtd->dma_buffer.bytes); 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_get_caps(struct snd_soc_component *component, 11468c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 11478c2ecf20Sopenharmony_ci struct snd_compr_caps *caps) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci caps->direction = SND_COMPRESS_PLAYBACK; 11508c2ecf20Sopenharmony_ci caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; 11518c2ecf20Sopenharmony_ci caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; 11528c2ecf20Sopenharmony_ci caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; 11538c2ecf20Sopenharmony_ci caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; 11548c2ecf20Sopenharmony_ci caps->num_codecs = 5; 11558c2ecf20Sopenharmony_ci caps->codecs[0] = SND_AUDIOCODEC_MP3; 11568c2ecf20Sopenharmony_ci caps->codecs[1] = SND_AUDIOCODEC_FLAC; 11578c2ecf20Sopenharmony_ci caps->codecs[2] = SND_AUDIOCODEC_WMA; 11588c2ecf20Sopenharmony_ci caps->codecs[3] = SND_AUDIOCODEC_ALAC; 11598c2ecf20Sopenharmony_ci caps->codecs[4] = SND_AUDIOCODEC_APE; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component, 11658c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 11668c2ecf20Sopenharmony_ci struct snd_compr_codec_caps *codec) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci switch (codec->codec) { 11698c2ecf20Sopenharmony_ci case SND_AUDIOCODEC_MP3: 11708c2ecf20Sopenharmony_ci *codec = q6asm_compr_caps; 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci default: 11738c2ecf20Sopenharmony_ci break; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic struct snd_compress_ops q6asm_dai_compress_ops = { 11808c2ecf20Sopenharmony_ci .open = q6asm_dai_compr_open, 11818c2ecf20Sopenharmony_ci .free = q6asm_dai_compr_free, 11828c2ecf20Sopenharmony_ci .set_params = q6asm_dai_compr_set_params, 11838c2ecf20Sopenharmony_ci .set_metadata = q6asm_dai_compr_set_metadata, 11848c2ecf20Sopenharmony_ci .pointer = q6asm_dai_compr_pointer, 11858c2ecf20Sopenharmony_ci .trigger = q6asm_dai_compr_trigger, 11868c2ecf20Sopenharmony_ci .get_caps = q6asm_dai_compr_get_caps, 11878c2ecf20Sopenharmony_ci .get_codec_caps = q6asm_dai_compr_get_codec_caps, 11888c2ecf20Sopenharmony_ci .mmap = q6asm_dai_compr_mmap, 11898c2ecf20Sopenharmony_ci .copy = q6asm_compr_copy, 11908c2ecf20Sopenharmony_ci}; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic int q6asm_dai_pcm_new(struct snd_soc_component *component, 11938c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct snd_pcm_substream *psubstream, *csubstream; 11968c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 11978c2ecf20Sopenharmony_ci struct device *dev; 11988c2ecf20Sopenharmony_ci int size, ret; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci dev = component->dev; 12018c2ecf20Sopenharmony_ci size = q6asm_dai_hardware_playback.buffer_bytes_max; 12028c2ecf20Sopenharmony_ci psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 12038c2ecf20Sopenharmony_ci if (psubstream) { 12048c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, 12058c2ecf20Sopenharmony_ci &psubstream->dma_buffer); 12068c2ecf20Sopenharmony_ci if (ret) { 12078c2ecf20Sopenharmony_ci dev_err(dev, "Cannot allocate buffer(s)\n"); 12088c2ecf20Sopenharmony_ci return ret; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 12138c2ecf20Sopenharmony_ci if (csubstream) { 12148c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, 12158c2ecf20Sopenharmony_ci &csubstream->dma_buffer); 12168c2ecf20Sopenharmony_ci if (ret) { 12178c2ecf20Sopenharmony_ci dev_err(dev, "Cannot allocate buffer(s)\n"); 12188c2ecf20Sopenharmony_ci if (psubstream) 12198c2ecf20Sopenharmony_ci snd_dma_free_pages(&psubstream->dma_buffer); 12208c2ecf20Sopenharmony_ci return ret; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic void q6asm_dai_pcm_free(struct snd_soc_component *component, 12288c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 12318c2ecf20Sopenharmony_ci int i; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { 12348c2ecf20Sopenharmony_ci substream = pcm->streams[i].substream; 12358c2ecf20Sopenharmony_ci if (substream) { 12368c2ecf20Sopenharmony_ci snd_dma_free_pages(&substream->dma_buffer); 12378c2ecf20Sopenharmony_ci substream->dma_buffer.area = NULL; 12388c2ecf20Sopenharmony_ci substream->dma_buffer.addr = 0; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget q6asm_dapm_widgets[] = { 12448c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, SND_SOC_NOPM, 0, 0), 12458c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, SND_SOC_NOPM, 0, 0), 12468c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, SND_SOC_NOPM, 0, 0), 12478c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, SND_SOC_NOPM, 0, 0), 12488c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, SND_SOC_NOPM, 0, 0), 12498c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, SND_SOC_NOPM, 0, 0), 12508c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, SND_SOC_NOPM, 0, 0), 12518c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, SND_SOC_NOPM, 0, 0), 12528c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, SND_SOC_NOPM, 0, 0), 12538c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, SND_SOC_NOPM, 0, 0), 12548c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, SND_SOC_NOPM, 0, 0), 12558c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, SND_SOC_NOPM, 0, 0), 12568c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, SND_SOC_NOPM, 0, 0), 12578c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, SND_SOC_NOPM, 0, 0), 12588c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, SND_SOC_NOPM, 0, 0), 12598c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, SND_SOC_NOPM, 0, 0), 12608c2ecf20Sopenharmony_ci}; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver q6asm_fe_dai_component = { 12638c2ecf20Sopenharmony_ci .name = DRV_NAME, 12648c2ecf20Sopenharmony_ci .open = q6asm_dai_open, 12658c2ecf20Sopenharmony_ci .hw_params = q6asm_dai_hw_params, 12668c2ecf20Sopenharmony_ci .close = q6asm_dai_close, 12678c2ecf20Sopenharmony_ci .prepare = q6asm_dai_prepare, 12688c2ecf20Sopenharmony_ci .trigger = q6asm_dai_trigger, 12698c2ecf20Sopenharmony_ci .pointer = q6asm_dai_pointer, 12708c2ecf20Sopenharmony_ci .mmap = q6asm_dai_mmap, 12718c2ecf20Sopenharmony_ci .pcm_construct = q6asm_dai_pcm_new, 12728c2ecf20Sopenharmony_ci .pcm_destruct = q6asm_dai_pcm_free, 12738c2ecf20Sopenharmony_ci .compress_ops = &q6asm_dai_compress_ops, 12748c2ecf20Sopenharmony_ci .dapm_widgets = q6asm_dapm_widgets, 12758c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets), 12768c2ecf20Sopenharmony_ci}; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver q6asm_fe_dais_template[] = { 12798c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(1), 12808c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(2), 12818c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(3), 12828c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(4), 12838c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(5), 12848c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(6), 12858c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(7), 12868c2ecf20Sopenharmony_ci Q6ASM_FEDAI_DRIVER(8), 12878c2ecf20Sopenharmony_ci}; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic int of_q6asm_parse_dai_data(struct device *dev, 12908c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 12938c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream empty_stream; 12948c2ecf20Sopenharmony_ci struct device_node *node; 12958c2ecf20Sopenharmony_ci int ret, id, dir, idx = 0; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci pdata->num_dais = of_get_child_count(dev->of_node); 12998c2ecf20Sopenharmony_ci if (!pdata->num_dais) { 13008c2ecf20Sopenharmony_ci dev_err(dev, "No dais found in DT\n"); 13018c2ecf20Sopenharmony_ci return -EINVAL; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv), 13058c2ecf20Sopenharmony_ci GFP_KERNEL); 13068c2ecf20Sopenharmony_ci if (!pdata->dais) 13078c2ecf20Sopenharmony_ci return -ENOMEM; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci memset(&empty_stream, 0, sizeof(empty_stream)); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 13128c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "reg", &id); 13138c2ecf20Sopenharmony_ci if (ret || id >= MAX_SESSIONS || id < 0) { 13148c2ecf20Sopenharmony_ci dev_err(dev, "valid dai id not found:%d\n", ret); 13158c2ecf20Sopenharmony_ci continue; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci dai_drv = &pdata->dais[idx++]; 13198c2ecf20Sopenharmony_ci *dai_drv = q6asm_fe_dais_template[id]; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "direction", &dir); 13228c2ecf20Sopenharmony_ci if (ret) 13238c2ecf20Sopenharmony_ci continue; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (dir == Q6ASM_DAI_RX) 13268c2ecf20Sopenharmony_ci dai_drv->capture = empty_stream; 13278c2ecf20Sopenharmony_ci else if (dir == Q6ASM_DAI_TX) 13288c2ecf20Sopenharmony_ci dai_drv->playback = empty_stream; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (of_property_read_bool(node, "is-compress-dai")) 13318c2ecf20Sopenharmony_ci dai_drv->compress_new = snd_soc_new_compress; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return 0; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int q6asm_dai_probe(struct platform_device *pdev) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13408c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 13418c2ecf20Sopenharmony_ci struct of_phandle_args args; 13428c2ecf20Sopenharmony_ci struct q6asm_dai_data *pdata; 13438c2ecf20Sopenharmony_ci int rc; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 13468c2ecf20Sopenharmony_ci if (!pdata) 13478c2ecf20Sopenharmony_ci return -ENOMEM; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); 13508c2ecf20Sopenharmony_ci if (rc < 0) 13518c2ecf20Sopenharmony_ci pdata->sid = -1; 13528c2ecf20Sopenharmony_ci else 13538c2ecf20Sopenharmony_ci pdata->sid = args.args[0] & SID_MASK_DEFAULT; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci dev_set_drvdata(dev, pdata); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci rc = of_q6asm_parse_dai_data(dev, pdata); 13588c2ecf20Sopenharmony_ci if (rc) 13598c2ecf20Sopenharmony_ci return rc; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, 13628c2ecf20Sopenharmony_ci pdata->dais, pdata->num_dais); 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 13668c2ecf20Sopenharmony_cistatic const struct of_device_id q6asm_dai_device_id[] = { 13678c2ecf20Sopenharmony_ci { .compatible = "qcom,q6asm-dais" }, 13688c2ecf20Sopenharmony_ci {}, 13698c2ecf20Sopenharmony_ci}; 13708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6asm_dai_device_id); 13718c2ecf20Sopenharmony_ci#endif 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cistatic struct platform_driver q6asm_dai_platform_driver = { 13748c2ecf20Sopenharmony_ci .driver = { 13758c2ecf20Sopenharmony_ci .name = "q6asm-dai", 13768c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(q6asm_dai_device_id), 13778c2ecf20Sopenharmony_ci }, 13788c2ecf20Sopenharmony_ci .probe = q6asm_dai_probe, 13798c2ecf20Sopenharmony_ci}; 13808c2ecf20Sopenharmony_cimodule_platform_driver(q6asm_dai_platform_driver); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Q6ASM dai driver"); 13838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1384