1322 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.3// Copyright (c) 2018, Linaro Limited4 5#include <dt-bindings/sound/qcom,q6asm.h>6#include <linux/init.h>7#include <linux/err.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/slab.h>12#include <sound/soc.h>13#include <sound/soc-dapm.h>14#include <sound/pcm.h>15#include <linux/spinlock.h>16#include <sound/compress_driver.h>17#include <asm/dma.h>18#include <linux/dma-mapping.h>19#include <sound/pcm_params.h>20#include "q6asm.h"21#include "q6routing.h"22#include "q6dsp-errno.h"23 24#define DRV_NAME "q6asm-fe-dai"25 26#define PLAYBACK_MIN_NUM_PERIODS 227#define PLAYBACK_MAX_NUM_PERIODS 828#define PLAYBACK_MAX_PERIOD_SIZE 6553629#define PLAYBACK_MIN_PERIOD_SIZE 12830#define CAPTURE_MIN_NUM_PERIODS 231#define CAPTURE_MAX_NUM_PERIODS 832#define CAPTURE_MAX_PERIOD_SIZE 409633#define CAPTURE_MIN_PERIOD_SIZE 32034#define SID_MASK_DEFAULT 0xF35 36/* Default values used if user space does not set */37#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)38#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)39#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)40#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)41 42#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1)43#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2)44 45enum stream_state {46 Q6ASM_STREAM_IDLE = 0,47 Q6ASM_STREAM_STOPPED,48 Q6ASM_STREAM_RUNNING,49};50 51struct q6asm_dai_rtd {52 struct snd_pcm_substream *substream;53 struct snd_compr_stream *cstream;54 struct snd_codec codec;55 struct snd_dma_buffer dma_buffer;56 spinlock_t lock;57 phys_addr_t phys;58 unsigned int pcm_size;59 unsigned int pcm_count;60 unsigned int pcm_irq_pos; /* IRQ position */61 unsigned int periods;62 unsigned int bytes_sent;63 unsigned int bytes_received;64 unsigned int copied_total;65 uint16_t bits_per_sample;66 uint16_t source; /* Encoding source bit mask */67 struct audio_client *audio_client;68 uint32_t next_track_stream_id;69 bool next_track;70 uint32_t stream_id;71 uint16_t session_id;72 enum stream_state state;73 uint32_t initial_samples_drop;74 uint32_t trailing_samples_drop;75 bool notify_on_drain;76};77 78struct q6asm_dai_data {79 struct snd_soc_dai_driver *dais;80 int num_dais;81 long long int sid;82};83 84static const struct snd_pcm_hardware q6asm_dai_hardware_capture = {85 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |86 SNDRV_PCM_INFO_BLOCK_TRANSFER |87 SNDRV_PCM_INFO_MMAP_VALID |88 SNDRV_PCM_INFO_INTERLEAVED |89 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),90 .formats = (SNDRV_PCM_FMTBIT_S16_LE |91 SNDRV_PCM_FMTBIT_S24_LE),92 .rates = SNDRV_PCM_RATE_8000_48000,93 .rate_min = 8000,94 .rate_max = 48000,95 .channels_min = 1,96 .channels_max = 4,97 .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *98 CAPTURE_MAX_PERIOD_SIZE,99 .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,100 .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,101 .periods_min = CAPTURE_MIN_NUM_PERIODS,102 .periods_max = CAPTURE_MAX_NUM_PERIODS,103 .fifo_size = 0,104};105 106static const struct snd_pcm_hardware q6asm_dai_hardware_playback = {107 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |108 SNDRV_PCM_INFO_BLOCK_TRANSFER |109 SNDRV_PCM_INFO_MMAP_VALID |110 SNDRV_PCM_INFO_INTERLEAVED |111 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),112 .formats = (SNDRV_PCM_FMTBIT_S16_LE |113 SNDRV_PCM_FMTBIT_S24_LE),114 .rates = SNDRV_PCM_RATE_8000_192000,115 .rate_min = 8000,116 .rate_max = 192000,117 .channels_min = 1,118 .channels_max = 8,119 .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS *120 PLAYBACK_MAX_PERIOD_SIZE),121 .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,122 .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,123 .periods_min = PLAYBACK_MIN_NUM_PERIODS,124 .periods_max = PLAYBACK_MAX_NUM_PERIODS,125 .fifo_size = 0,126};127 128#define Q6ASM_FEDAI_DRIVER(num) { \129 .playback = { \130 .stream_name = "MultiMedia"#num" Playback", \131 .rates = (SNDRV_PCM_RATE_8000_48000 | \132 SNDRV_PCM_RATE_12000 | \133 SNDRV_PCM_RATE_24000 | \134 SNDRV_PCM_RATE_88200 | \135 SNDRV_PCM_RATE_96000 | \136 SNDRV_PCM_RATE_176400 | \137 SNDRV_PCM_RATE_192000), \138 .formats = (SNDRV_PCM_FMTBIT_S16_LE | \139 SNDRV_PCM_FMTBIT_S24_LE), \140 .channels_min = 1, \141 .channels_max = 8, \142 .rate_min = 8000, \143 .rate_max = 192000, \144 }, \145 .capture = { \146 .stream_name = "MultiMedia"#num" Capture", \147 .rates = (SNDRV_PCM_RATE_8000_48000 | \148 SNDRV_PCM_RATE_12000 | \149 SNDRV_PCM_RATE_24000), \150 .formats = (SNDRV_PCM_FMTBIT_S16_LE | \151 SNDRV_PCM_FMTBIT_S24_LE), \152 .channels_min = 1, \153 .channels_max = 4, \154 .rate_min = 8000, \155 .rate_max = 48000, \156 }, \157 .name = "MultiMedia"#num, \158 .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \159 }160 161static const struct snd_compr_codec_caps q6asm_compr_caps = {162 .num_descriptors = 1,163 .descriptor[0].max_ch = 2,164 .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,165 24000, 32000, 44100, 48000, 88200,166 96000, 176400, 192000 },167 .descriptor[0].num_sample_rates = 13,168 .descriptor[0].bit_rate[0] = 320,169 .descriptor[0].bit_rate[1] = 128,170 .descriptor[0].num_bitrates = 2,171 .descriptor[0].profiles = 0,172 .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,173 .descriptor[0].formats = 0,174};175 176static void event_handler(uint32_t opcode, uint32_t token,177 void *payload, void *priv)178{179 struct q6asm_dai_rtd *prtd = priv;180 struct snd_pcm_substream *substream = prtd->substream;181 182 switch (opcode) {183 case ASM_CLIENT_EVENT_CMD_RUN_DONE:184 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)185 q6asm_write_async(prtd->audio_client, prtd->stream_id,186 prtd->pcm_count, 0, 0, 0);187 break;188 case ASM_CLIENT_EVENT_CMD_EOS_DONE:189 prtd->state = Q6ASM_STREAM_STOPPED;190 break;191 case ASM_CLIENT_EVENT_DATA_WRITE_DONE: {192 prtd->pcm_irq_pos += prtd->pcm_count;193 snd_pcm_period_elapsed(substream);194 if (prtd->state == Q6ASM_STREAM_RUNNING)195 q6asm_write_async(prtd->audio_client, prtd->stream_id,196 prtd->pcm_count, 0, 0, 0);197 198 break;199 }200 case ASM_CLIENT_EVENT_DATA_READ_DONE:201 prtd->pcm_irq_pos += prtd->pcm_count;202 snd_pcm_period_elapsed(substream);203 if (prtd->state == Q6ASM_STREAM_RUNNING)204 q6asm_read(prtd->audio_client, prtd->stream_id);205 206 break;207 default:208 break;209 }210}211 212static int q6asm_dai_prepare(struct snd_soc_component *component,213 struct snd_pcm_substream *substream)214{215 struct snd_pcm_runtime *runtime = substream->runtime;216 struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);217 struct q6asm_dai_rtd *prtd = runtime->private_data;218 struct q6asm_dai_data *pdata;219 struct device *dev = component->dev;220 int ret, i;221 222 pdata = snd_soc_component_get_drvdata(component);223 if (!pdata)224 return -EINVAL;225 226 if (!prtd || !prtd->audio_client) {227 dev_err(dev, "%s: private data null or audio client freed\n",228 __func__);229 return -EINVAL;230 }231 232 prtd->pcm_count = snd_pcm_lib_period_bytes(substream);233 prtd->pcm_irq_pos = 0;234 /* rate and channels are sent to audio driver */235 if (prtd->state) {236 /* clear the previous setup if any */237 q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);238 q6asm_unmap_memory_regions(substream->stream,239 prtd->audio_client);240 q6routing_stream_close(soc_prtd->dai_link->id,241 substream->stream);242 }243 244 ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client,245 prtd->phys,246 (prtd->pcm_size / prtd->periods),247 prtd->periods);248 249 if (ret < 0) {250 dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n",251 ret);252 return -ENOMEM;253 }254 255 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {256 ret = q6asm_open_write(prtd->audio_client, prtd->stream_id,257 FORMAT_LINEAR_PCM,258 0, prtd->bits_per_sample, false);259 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {260 ret = q6asm_open_read(prtd->audio_client, prtd->stream_id,261 FORMAT_LINEAR_PCM,262 prtd->bits_per_sample);263 }264 265 if (ret < 0) {266 dev_err(dev, "%s: q6asm_open_write failed\n", __func__);267 goto open_err;268 }269 270 prtd->session_id = q6asm_get_session_id(prtd->audio_client);271 ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE,272 prtd->session_id, substream->stream);273 if (ret) {274 dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret);275 goto routing_err;276 }277 278 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {279 ret = q6asm_media_format_block_multi_ch_pcm(280 prtd->audio_client, prtd->stream_id,281 runtime->rate, runtime->channels, NULL,282 prtd->bits_per_sample);283 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {284 ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client,285 prtd->stream_id,286 runtime->rate,287 runtime->channels,288 prtd->bits_per_sample);289 290 /* Queue the buffers */291 for (i = 0; i < runtime->periods; i++)292 q6asm_read(prtd->audio_client, prtd->stream_id);293 294 }295 if (ret < 0)296 dev_info(dev, "%s: CMD Format block failed\n", __func__);297 else298 prtd->state = Q6ASM_STREAM_RUNNING;299 300 return ret;301 302routing_err:303 q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);304open_err:305 q6asm_unmap_memory_regions(substream->stream, prtd->audio_client);306 q6asm_audio_client_free(prtd->audio_client);307 prtd->audio_client = NULL;308 309 return ret;310}311 312static int q6asm_dai_trigger(struct snd_soc_component *component,313 struct snd_pcm_substream *substream, int cmd)314{315 int ret = 0;316 struct snd_pcm_runtime *runtime = substream->runtime;317 struct q6asm_dai_rtd *prtd = runtime->private_data;318 319 switch (cmd) {320 case SNDRV_PCM_TRIGGER_START:321 case SNDRV_PCM_TRIGGER_RESUME:322 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:323 ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,324 0, 0, 0);325 break;326 case SNDRV_PCM_TRIGGER_STOP:327 prtd->state = Q6ASM_STREAM_STOPPED;328 ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,329 CMD_EOS);330 break;331 case SNDRV_PCM_TRIGGER_SUSPEND:332 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:333 ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,334 CMD_PAUSE);335 break;336 default:337 ret = -EINVAL;338 break;339 }340 341 return ret;342}343 344static int q6asm_dai_open(struct snd_soc_component *component,345 struct snd_pcm_substream *substream)346{347 struct snd_pcm_runtime *runtime = substream->runtime;348 struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);349 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0);350 struct q6asm_dai_rtd *prtd;351 struct q6asm_dai_data *pdata;352 struct device *dev = component->dev;353 int ret = 0;354 int stream_id;355 356 stream_id = cpu_dai->driver->id;357 358 pdata = snd_soc_component_get_drvdata(component);359 if (!pdata) {360 dev_err(dev, "Drv data not found ..\n");361 return -EINVAL;362 }363 364 prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL);365 if (prtd == NULL)366 return -ENOMEM;367 368 prtd->substream = substream;369 prtd->audio_client = q6asm_audio_client_alloc(dev,370 (q6asm_cb)event_handler, prtd, stream_id,371 LEGACY_PCM_MODE);372 if (IS_ERR(prtd->audio_client)) {373 dev_info(dev, "%s: Could not allocate memory\n", __func__);374 ret = PTR_ERR(prtd->audio_client);375 kfree(prtd);376 return ret;377 }378 379 /* DSP expects stream id from 1 */380 prtd->stream_id = 1;381 382 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)383 runtime->hw = q6asm_dai_hardware_playback;384 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)385 runtime->hw = q6asm_dai_hardware_capture;386 387 /* Ensure that buffer size is a multiple of period size */388 ret = snd_pcm_hw_constraint_integer(runtime,389 SNDRV_PCM_HW_PARAM_PERIODS);390 if (ret < 0)391 dev_info(dev, "snd_pcm_hw_constraint_integer failed\n");392 393 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {394 ret = snd_pcm_hw_constraint_minmax(runtime,395 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,396 PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,397 PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);398 if (ret < 0) {399 dev_err(dev, "constraint for buffer bytes min max ret = %d\n",400 ret);401 }402 }403 404 ret = snd_pcm_hw_constraint_step(runtime, 0,405 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);406 if (ret < 0) {407 dev_err(dev, "constraint for period bytes step ret = %d\n",408 ret);409 }410 ret = snd_pcm_hw_constraint_step(runtime, 0,411 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);412 if (ret < 0) {413 dev_err(dev, "constraint for buffer bytes step ret = %d\n",414 ret);415 }416 417 runtime->private_data = prtd;418 419 snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback);420 421 runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max;422 423 424 if (pdata->sid < 0)425 prtd->phys = substream->dma_buffer.addr;426 else427 prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);428 429 return 0;430}431 432static int q6asm_dai_close(struct snd_soc_component *component,433 struct snd_pcm_substream *substream)434{435 struct snd_pcm_runtime *runtime = substream->runtime;436 struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);437 struct q6asm_dai_rtd *prtd = runtime->private_data;438 439 if (prtd->audio_client) {440 if (prtd->state)441 q6asm_cmd(prtd->audio_client, prtd->stream_id,442 CMD_CLOSE);443 444 q6asm_unmap_memory_regions(substream->stream,445 prtd->audio_client);446 q6asm_audio_client_free(prtd->audio_client);447 prtd->audio_client = NULL;448 }449 q6routing_stream_close(soc_prtd->dai_link->id,450 substream->stream);451 kfree(prtd);452 return 0;453}454 455static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component,456 struct snd_pcm_substream *substream)457{458 459 struct snd_pcm_runtime *runtime = substream->runtime;460 struct q6asm_dai_rtd *prtd = runtime->private_data;461 462 if (prtd->pcm_irq_pos >= prtd->pcm_size)463 prtd->pcm_irq_pos = 0;464 465 return bytes_to_frames(runtime, (prtd->pcm_irq_pos));466}467 468static int q6asm_dai_hw_params(struct snd_soc_component *component,469 struct snd_pcm_substream *substream,470 struct snd_pcm_hw_params *params)471{472 struct snd_pcm_runtime *runtime = substream->runtime;473 struct q6asm_dai_rtd *prtd = runtime->private_data;474 475 prtd->pcm_size = params_buffer_bytes(params);476 prtd->periods = params_periods(params);477 478 switch (params_format(params)) {479 case SNDRV_PCM_FORMAT_S16_LE:480 prtd->bits_per_sample = 16;481 break;482 case SNDRV_PCM_FORMAT_S24_LE:483 prtd->bits_per_sample = 24;484 break;485 }486 487 return 0;488}489 490static void compress_event_handler(uint32_t opcode, uint32_t token,491 void *payload, void *priv)492{493 struct q6asm_dai_rtd *prtd = priv;494 struct snd_compr_stream *substream = prtd->cstream;495 unsigned long flags;496 u32 wflags = 0;497 uint64_t avail;498 uint32_t bytes_written, bytes_to_write;499 bool is_last_buffer = false;500 501 switch (opcode) {502 case ASM_CLIENT_EVENT_CMD_RUN_DONE:503 spin_lock_irqsave(&prtd->lock, flags);504 if (!prtd->bytes_sent) {505 q6asm_stream_remove_initial_silence(prtd->audio_client,506 prtd->stream_id,507 prtd->initial_samples_drop);508 509 q6asm_write_async(prtd->audio_client, prtd->stream_id,510 prtd->pcm_count, 0, 0, 0);511 prtd->bytes_sent += prtd->pcm_count;512 }513 514 spin_unlock_irqrestore(&prtd->lock, flags);515 break;516 517 case ASM_CLIENT_EVENT_CMD_EOS_DONE:518 spin_lock_irqsave(&prtd->lock, flags);519 if (prtd->notify_on_drain) {520 if (substream->partial_drain) {521 /*522 * Close old stream and make it stale, switch523 * the active stream now!524 */525 q6asm_cmd_nowait(prtd->audio_client,526 prtd->stream_id,527 CMD_CLOSE);528 /*529 * vaild stream ids start from 1, So we are530 * toggling this between 1 and 2.531 */532 prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1);533 }534 535 snd_compr_drain_notify(prtd->cstream);536 prtd->notify_on_drain = false;537 538 } else {539 prtd->state = Q6ASM_STREAM_STOPPED;540 }541 spin_unlock_irqrestore(&prtd->lock, flags);542 break;543 544 case ASM_CLIENT_EVENT_DATA_WRITE_DONE:545 spin_lock_irqsave(&prtd->lock, flags);546 547 bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT;548 prtd->copied_total += bytes_written;549 snd_compr_fragment_elapsed(substream);550 551 if (prtd->state != Q6ASM_STREAM_RUNNING) {552 spin_unlock_irqrestore(&prtd->lock, flags);553 break;554 }555 556 avail = prtd->bytes_received - prtd->bytes_sent;557 if (avail > prtd->pcm_count) {558 bytes_to_write = prtd->pcm_count;559 } else {560 if (substream->partial_drain || prtd->notify_on_drain)561 is_last_buffer = true;562 bytes_to_write = avail;563 }564 565 if (bytes_to_write) {566 if (substream->partial_drain && is_last_buffer) {567 wflags |= ASM_LAST_BUFFER_FLAG;568 q6asm_stream_remove_trailing_silence(prtd->audio_client,569 prtd->stream_id,570 prtd->trailing_samples_drop);571 }572 573 q6asm_write_async(prtd->audio_client, prtd->stream_id,574 bytes_to_write, 0, 0, wflags);575 576 prtd->bytes_sent += bytes_to_write;577 }578 579 if (prtd->notify_on_drain && is_last_buffer)580 q6asm_cmd_nowait(prtd->audio_client,581 prtd->stream_id, CMD_EOS);582 583 spin_unlock_irqrestore(&prtd->lock, flags);584 break;585 586 default:587 break;588 }589}590 591static int q6asm_dai_compr_open(struct snd_soc_component *component,592 struct snd_compr_stream *stream)593{594 struct snd_soc_pcm_runtime *rtd = stream->private_data;595 struct snd_compr_runtime *runtime = stream->runtime;596 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);597 struct q6asm_dai_data *pdata;598 struct device *dev = component->dev;599 struct q6asm_dai_rtd *prtd;600 int stream_id, size, ret;601 602 stream_id = cpu_dai->driver->id;603 pdata = snd_soc_component_get_drvdata(component);604 if (!pdata) {605 dev_err(dev, "Drv data not found ..\n");606 return -EINVAL;607 }608 609 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);610 if (!prtd)611 return -ENOMEM;612 613 /* DSP expects stream id from 1 */614 prtd->stream_id = 1;615 616 prtd->cstream = stream;617 prtd->audio_client = q6asm_audio_client_alloc(dev,618 (q6asm_cb)compress_event_handler,619 prtd, stream_id, LEGACY_PCM_MODE);620 if (IS_ERR(prtd->audio_client)) {621 dev_err(dev, "Could not allocate memory\n");622 ret = PTR_ERR(prtd->audio_client);623 goto free_prtd;624 }625 626 size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *627 COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;628 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,629 &prtd->dma_buffer);630 if (ret) {631 dev_err(dev, "Cannot allocate buffer(s)\n");632 goto free_client;633 }634 635 if (pdata->sid < 0)636 prtd->phys = prtd->dma_buffer.addr;637 else638 prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);639 640 snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);641 spin_lock_init(&prtd->lock);642 runtime->private_data = prtd;643 644 return 0;645 646free_client:647 q6asm_audio_client_free(prtd->audio_client);648free_prtd:649 kfree(prtd);650 651 return ret;652}653 654static int q6asm_dai_compr_free(struct snd_soc_component *component,655 struct snd_compr_stream *stream)656{657 struct snd_compr_runtime *runtime = stream->runtime;658 struct q6asm_dai_rtd *prtd = runtime->private_data;659 struct snd_soc_pcm_runtime *rtd = stream->private_data;660 661 if (prtd->audio_client) {662 if (prtd->state) {663 q6asm_cmd(prtd->audio_client, prtd->stream_id,664 CMD_CLOSE);665 if (prtd->next_track_stream_id) {666 q6asm_cmd(prtd->audio_client,667 prtd->next_track_stream_id,668 CMD_CLOSE);669 }670 }671 672 snd_dma_free_pages(&prtd->dma_buffer);673 q6asm_unmap_memory_regions(stream->direction,674 prtd->audio_client);675 q6asm_audio_client_free(prtd->audio_client);676 prtd->audio_client = NULL;677 }678 q6routing_stream_close(rtd->dai_link->id, stream->direction);679 kfree(prtd);680 681 return 0;682}683 684static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component,685 struct snd_compr_stream *stream,686 struct snd_codec *codec,687 int stream_id)688{689 struct snd_compr_runtime *runtime = stream->runtime;690 struct q6asm_dai_rtd *prtd = runtime->private_data;691 struct q6asm_flac_cfg flac_cfg;692 struct q6asm_wma_cfg wma_cfg;693 struct q6asm_alac_cfg alac_cfg;694 struct q6asm_ape_cfg ape_cfg;695 unsigned int wma_v9 = 0;696 struct device *dev = component->dev;697 int ret;698 union snd_codec_options *codec_options;699 struct snd_dec_flac *flac;700 struct snd_dec_wma *wma;701 struct snd_dec_alac *alac;702 struct snd_dec_ape *ape;703 704 codec_options = &(prtd->codec.options);705 706 memcpy(&prtd->codec, codec, sizeof(*codec));707 708 switch (codec->id) {709 case SND_AUDIOCODEC_FLAC:710 711 memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg));712 flac = &codec_options->flac_d;713 714 flac_cfg.ch_cfg = codec->ch_in;715 flac_cfg.sample_rate = codec->sample_rate;716 flac_cfg.stream_info_present = 1;717 flac_cfg.sample_size = flac->sample_size;718 flac_cfg.min_blk_size = flac->min_blk_size;719 flac_cfg.max_blk_size = flac->max_blk_size;720 flac_cfg.max_frame_size = flac->max_frame_size;721 flac_cfg.min_frame_size = flac->min_frame_size;722 723 ret = q6asm_stream_media_format_block_flac(prtd->audio_client,724 stream_id,725 &flac_cfg);726 if (ret < 0) {727 dev_err(dev, "FLAC CMD Format block failed:%d\n", ret);728 return -EIO;729 }730 break;731 732 case SND_AUDIOCODEC_WMA:733 wma = &codec_options->wma_d;734 735 memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg));736 737 wma_cfg.sample_rate = codec->sample_rate;738 wma_cfg.num_channels = codec->ch_in;739 wma_cfg.bytes_per_sec = codec->bit_rate / 8;740 wma_cfg.block_align = codec->align;741 wma_cfg.bits_per_sample = prtd->bits_per_sample;742 wma_cfg.enc_options = wma->encoder_option;743 wma_cfg.adv_enc_options = wma->adv_encoder_option;744 wma_cfg.adv_enc_options2 = wma->adv_encoder_option2;745 746 if (wma_cfg.num_channels == 1)747 wma_cfg.channel_mask = 4; /* Mono Center */748 else if (wma_cfg.num_channels == 2)749 wma_cfg.channel_mask = 3; /* Stereo FL/FR */750 else751 return -EINVAL;752 753 /* check the codec profile */754 switch (codec->profile) {755 case SND_AUDIOPROFILE_WMA9:756 wma_cfg.fmtag = 0x161;757 wma_v9 = 1;758 break;759 760 case SND_AUDIOPROFILE_WMA10:761 wma_cfg.fmtag = 0x166;762 break;763 764 case SND_AUDIOPROFILE_WMA9_PRO:765 wma_cfg.fmtag = 0x162;766 break;767 768 case SND_AUDIOPROFILE_WMA9_LOSSLESS:769 wma_cfg.fmtag = 0x163;770 break;771 772 case SND_AUDIOPROFILE_WMA10_LOSSLESS:773 wma_cfg.fmtag = 0x167;774 break;775 776 default:777 dev_err(dev, "Unknown WMA profile:%x\n",778 codec->profile);779 return -EIO;780 }781 782 if (wma_v9)783 ret = q6asm_stream_media_format_block_wma_v9(784 prtd->audio_client, stream_id,785 &wma_cfg);786 else787 ret = q6asm_stream_media_format_block_wma_v10(788 prtd->audio_client, stream_id,789 &wma_cfg);790 if (ret < 0) {791 dev_err(dev, "WMA9 CMD failed:%d\n", ret);792 return -EIO;793 }794 break;795 796 case SND_AUDIOCODEC_ALAC:797 memset(&alac_cfg, 0x0, sizeof(alac_cfg));798 alac = &codec_options->alac_d;799 800 alac_cfg.sample_rate = codec->sample_rate;801 alac_cfg.avg_bit_rate = codec->bit_rate;802 alac_cfg.bit_depth = prtd->bits_per_sample;803 alac_cfg.num_channels = codec->ch_in;804 805 alac_cfg.frame_length = alac->frame_length;806 alac_cfg.pb = alac->pb;807 alac_cfg.mb = alac->mb;808 alac_cfg.kb = alac->kb;809 alac_cfg.max_run = alac->max_run;810 alac_cfg.compatible_version = alac->compatible_version;811 alac_cfg.max_frame_bytes = alac->max_frame_bytes;812 813 switch (codec->ch_in) {814 case 1:815 alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO;816 break;817 case 2:818 alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO;819 break;820 }821 ret = q6asm_stream_media_format_block_alac(prtd->audio_client,822 stream_id,823 &alac_cfg);824 if (ret < 0) {825 dev_err(dev, "ALAC CMD Format block failed:%d\n", ret);826 return -EIO;827 }828 break;829 830 case SND_AUDIOCODEC_APE:831 memset(&ape_cfg, 0x0, sizeof(ape_cfg));832 ape = &codec_options->ape_d;833 834 ape_cfg.sample_rate = codec->sample_rate;835 ape_cfg.num_channels = codec->ch_in;836 ape_cfg.bits_per_sample = prtd->bits_per_sample;837 838 ape_cfg.compatible_version = ape->compatible_version;839 ape_cfg.compression_level = ape->compression_level;840 ape_cfg.format_flags = ape->format_flags;841 ape_cfg.blocks_per_frame = ape->blocks_per_frame;842 ape_cfg.final_frame_blocks = ape->final_frame_blocks;843 ape_cfg.total_frames = ape->total_frames;844 ape_cfg.seek_table_present = ape->seek_table_present;845 846 ret = q6asm_stream_media_format_block_ape(prtd->audio_client,847 stream_id,848 &ape_cfg);849 if (ret < 0) {850 dev_err(dev, "APE CMD Format block failed:%d\n", ret);851 return -EIO;852 }853 break;854 855 default:856 break;857 }858 859 return 0;860}861 862static int q6asm_dai_compr_set_params(struct snd_soc_component *component,863 struct snd_compr_stream *stream,864 struct snd_compr_params *params)865{866 struct snd_compr_runtime *runtime = stream->runtime;867 struct q6asm_dai_rtd *prtd = runtime->private_data;868 struct snd_soc_pcm_runtime *rtd = stream->private_data;869 int dir = stream->direction;870 struct q6asm_dai_data *pdata;871 struct device *dev = component->dev;872 int ret;873 874 pdata = snd_soc_component_get_drvdata(component);875 if (!pdata)876 return -EINVAL;877 878 if (!prtd || !prtd->audio_client) {879 dev_err(dev, "private data null or audio client freed\n");880 return -EINVAL;881 }882 883 prtd->periods = runtime->fragments;884 prtd->pcm_count = runtime->fragment_size;885 prtd->pcm_size = runtime->fragments * runtime->fragment_size;886 prtd->bits_per_sample = 16;887 888 if (dir == SND_COMPRESS_PLAYBACK) {889 ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id,890 params->codec.profile, prtd->bits_per_sample,891 true);892 893 if (ret < 0) {894 dev_err(dev, "q6asm_open_write failed\n");895 q6asm_audio_client_free(prtd->audio_client);896 prtd->audio_client = NULL;897 return ret;898 }899 }900 901 prtd->session_id = q6asm_get_session_id(prtd->audio_client);902 ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE,903 prtd->session_id, dir);904 if (ret) {905 dev_err(dev, "Stream reg failed ret:%d\n", ret);906 return ret;907 }908 909 ret = __q6asm_dai_compr_set_codec_params(component, stream,910 ¶ms->codec,911 prtd->stream_id);912 if (ret) {913 dev_err(dev, "codec param setup failed ret:%d\n", ret);914 return ret;915 }916 917 ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys,918 (prtd->pcm_size / prtd->periods),919 prtd->periods);920 921 if (ret < 0) {922 dev_err(dev, "Buffer Mapping failed ret:%d\n", ret);923 return -ENOMEM;924 }925 926 prtd->state = Q6ASM_STREAM_RUNNING;927 928 return 0;929}930 931static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component,932 struct snd_compr_stream *stream,933 struct snd_compr_metadata *metadata)934{935 struct snd_compr_runtime *runtime = stream->runtime;936 struct q6asm_dai_rtd *prtd = runtime->private_data;937 int ret = 0;938 939 switch (metadata->key) {940 case SNDRV_COMPRESS_ENCODER_PADDING:941 prtd->trailing_samples_drop = metadata->value[0];942 break;943 case SNDRV_COMPRESS_ENCODER_DELAY:944 prtd->initial_samples_drop = metadata->value[0];945 if (prtd->next_track_stream_id) {946 ret = q6asm_open_write(prtd->audio_client,947 prtd->next_track_stream_id,948 prtd->codec.id,949 prtd->codec.profile,950 prtd->bits_per_sample,951 true);952 if (ret < 0) {953 dev_err(component->dev, "q6asm_open_write failed\n");954 return ret;955 }956 ret = __q6asm_dai_compr_set_codec_params(component, stream,957 &prtd->codec,958 prtd->next_track_stream_id);959 if (ret < 0) {960 dev_err(component->dev, "q6asm_open_write failed\n");961 return ret;962 }963 964 ret = q6asm_stream_remove_initial_silence(prtd->audio_client,965 prtd->next_track_stream_id,966 prtd->initial_samples_drop);967 prtd->next_track_stream_id = 0;968 969 }970 971 break;972 default:973 ret = -EINVAL;974 break;975 }976 977 return ret;978}979 980static int q6asm_dai_compr_trigger(struct snd_soc_component *component,981 struct snd_compr_stream *stream, int cmd)982{983 struct snd_compr_runtime *runtime = stream->runtime;984 struct q6asm_dai_rtd *prtd = runtime->private_data;985 int ret = 0;986 987 switch (cmd) {988 case SNDRV_PCM_TRIGGER_START:989 case SNDRV_PCM_TRIGGER_RESUME:990 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:991 ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,992 0, 0, 0);993 break;994 case SNDRV_PCM_TRIGGER_STOP:995 prtd->state = Q6ASM_STREAM_STOPPED;996 ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,997 CMD_EOS);998 break;999 case SNDRV_PCM_TRIGGER_SUSPEND:1000 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:1001 ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,1002 CMD_PAUSE);1003 break;1004 case SND_COMPR_TRIGGER_NEXT_TRACK:1005 prtd->next_track = true;1006 prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1);1007 break;1008 case SND_COMPR_TRIGGER_DRAIN:1009 case SND_COMPR_TRIGGER_PARTIAL_DRAIN:1010 prtd->notify_on_drain = true;1011 break;1012 default:1013 ret = -EINVAL;1014 break;1015 }1016 1017 return ret;1018}1019 1020static int q6asm_dai_compr_pointer(struct snd_soc_component *component,1021 struct snd_compr_stream *stream,1022 struct snd_compr_tstamp *tstamp)1023{1024 struct snd_compr_runtime *runtime = stream->runtime;1025 struct q6asm_dai_rtd *prtd = runtime->private_data;1026 unsigned long flags;1027 1028 spin_lock_irqsave(&prtd->lock, flags);1029 1030 tstamp->copied_total = prtd->copied_total;1031 tstamp->byte_offset = prtd->copied_total % prtd->pcm_size;1032 1033 spin_unlock_irqrestore(&prtd->lock, flags);1034 1035 return 0;1036}1037 1038static int q6asm_compr_copy(struct snd_soc_component *component,1039 struct snd_compr_stream *stream, char __user *buf,1040 size_t count)1041{1042 struct snd_compr_runtime *runtime = stream->runtime;1043 struct q6asm_dai_rtd *prtd = runtime->private_data;1044 unsigned long flags;1045 u32 wflags = 0;1046 int avail, bytes_in_flight = 0;1047 void *dstn;1048 size_t copy;1049 u32 app_pointer;1050 u32 bytes_received;1051 1052 bytes_received = prtd->bytes_received;1053 1054 /**1055 * Make sure that next track data pointer is aligned at 32 bit boundary1056 * This is a Mandatory requirement from DSP data buffers alignment1057 */1058 if (prtd->next_track)1059 bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);1060 1061 app_pointer = bytes_received/prtd->pcm_size;1062 app_pointer = bytes_received - (app_pointer * prtd->pcm_size);1063 dstn = prtd->dma_buffer.area + app_pointer;1064 1065 if (count < prtd->pcm_size - app_pointer) {1066 if (copy_from_user(dstn, buf, count))1067 return -EFAULT;1068 } else {1069 copy = prtd->pcm_size - app_pointer;1070 if (copy_from_user(dstn, buf, copy))1071 return -EFAULT;1072 if (copy_from_user(prtd->dma_buffer.area, buf + copy,1073 count - copy))1074 return -EFAULT;1075 }1076 1077 spin_lock_irqsave(&prtd->lock, flags);1078 1079 bytes_in_flight = prtd->bytes_received - prtd->copied_total;1080 1081 if (prtd->next_track) {1082 prtd->next_track = false;1083 prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);1084 prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);1085 }1086 1087 prtd->bytes_received = bytes_received + count;1088 1089 /* Kick off the data to dsp if its starving!! */1090 if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) {1091 uint32_t bytes_to_write = prtd->pcm_count;1092 1093 avail = prtd->bytes_received - prtd->bytes_sent;1094 1095 if (avail < prtd->pcm_count)1096 bytes_to_write = avail;1097 1098 q6asm_write_async(prtd->audio_client, prtd->stream_id,1099 bytes_to_write, 0, 0, wflags);1100 prtd->bytes_sent += bytes_to_write;1101 }1102 1103 spin_unlock_irqrestore(&prtd->lock, flags);1104 1105 return count;1106}1107 1108static int q6asm_dai_compr_mmap(struct snd_soc_component *component,1109 struct snd_compr_stream *stream,1110 struct vm_area_struct *vma)1111{1112 struct snd_compr_runtime *runtime = stream->runtime;1113 struct q6asm_dai_rtd *prtd = runtime->private_data;1114 struct device *dev = component->dev;1115 1116 return dma_mmap_coherent(dev, vma,1117 prtd->dma_buffer.area, prtd->dma_buffer.addr,1118 prtd->dma_buffer.bytes);1119}1120 1121static int q6asm_dai_compr_get_caps(struct snd_soc_component *component,1122 struct snd_compr_stream *stream,1123 struct snd_compr_caps *caps)1124{1125 caps->direction = SND_COMPRESS_PLAYBACK;1126 caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;1127 caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;1128 caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;1129 caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;1130 caps->num_codecs = 5;1131 caps->codecs[0] = SND_AUDIOCODEC_MP3;1132 caps->codecs[1] = SND_AUDIOCODEC_FLAC;1133 caps->codecs[2] = SND_AUDIOCODEC_WMA;1134 caps->codecs[3] = SND_AUDIOCODEC_ALAC;1135 caps->codecs[4] = SND_AUDIOCODEC_APE;1136 1137 return 0;1138}1139 1140static int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component,1141 struct snd_compr_stream *stream,1142 struct snd_compr_codec_caps *codec)1143{1144 switch (codec->codec) {1145 case SND_AUDIOCODEC_MP3:1146 *codec = q6asm_compr_caps;1147 break;1148 default:1149 break;1150 }1151 1152 return 0;1153}1154 1155static const struct snd_compress_ops q6asm_dai_compress_ops = {1156 .open = q6asm_dai_compr_open,1157 .free = q6asm_dai_compr_free,1158 .set_params = q6asm_dai_compr_set_params,1159 .set_metadata = q6asm_dai_compr_set_metadata,1160 .pointer = q6asm_dai_compr_pointer,1161 .trigger = q6asm_dai_compr_trigger,1162 .get_caps = q6asm_dai_compr_get_caps,1163 .get_codec_caps = q6asm_dai_compr_get_codec_caps,1164 .mmap = q6asm_dai_compr_mmap,1165 .copy = q6asm_compr_copy,1166};1167 1168static int q6asm_dai_pcm_new(struct snd_soc_component *component,1169 struct snd_soc_pcm_runtime *rtd)1170{1171 struct snd_pcm *pcm = rtd->pcm;1172 size_t size = q6asm_dai_hardware_playback.buffer_bytes_max;1173 1174 return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,1175 component->dev, size);1176}1177 1178static const struct snd_soc_dapm_widget q6asm_dapm_widgets[] = {1179 SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, SND_SOC_NOPM, 0, 0),1180 SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, SND_SOC_NOPM, 0, 0),1181 SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, SND_SOC_NOPM, 0, 0),1182 SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, SND_SOC_NOPM, 0, 0),1183 SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, SND_SOC_NOPM, 0, 0),1184 SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, SND_SOC_NOPM, 0, 0),1185 SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, SND_SOC_NOPM, 0, 0),1186 SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, SND_SOC_NOPM, 0, 0),1187 SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, SND_SOC_NOPM, 0, 0),1188 SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, SND_SOC_NOPM, 0, 0),1189 SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, SND_SOC_NOPM, 0, 0),1190 SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, SND_SOC_NOPM, 0, 0),1191 SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, SND_SOC_NOPM, 0, 0),1192 SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, SND_SOC_NOPM, 0, 0),1193 SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, SND_SOC_NOPM, 0, 0),1194 SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, SND_SOC_NOPM, 0, 0),1195};1196 1197static const struct snd_soc_component_driver q6asm_fe_dai_component = {1198 .name = DRV_NAME,1199 .open = q6asm_dai_open,1200 .hw_params = q6asm_dai_hw_params,1201 .close = q6asm_dai_close,1202 .prepare = q6asm_dai_prepare,1203 .trigger = q6asm_dai_trigger,1204 .pointer = q6asm_dai_pointer,1205 .pcm_construct = q6asm_dai_pcm_new,1206 .compress_ops = &q6asm_dai_compress_ops,1207 .dapm_widgets = q6asm_dapm_widgets,1208 .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets),1209 .legacy_dai_naming = 1,1210};1211 1212static struct snd_soc_dai_driver q6asm_fe_dais_template[] = {1213 Q6ASM_FEDAI_DRIVER(1),1214 Q6ASM_FEDAI_DRIVER(2),1215 Q6ASM_FEDAI_DRIVER(3),1216 Q6ASM_FEDAI_DRIVER(4),1217 Q6ASM_FEDAI_DRIVER(5),1218 Q6ASM_FEDAI_DRIVER(6),1219 Q6ASM_FEDAI_DRIVER(7),1220 Q6ASM_FEDAI_DRIVER(8),1221};1222 1223static const struct snd_soc_dai_ops q6asm_dai_ops = {1224 .compress_new = snd_soc_new_compress,1225};1226 1227static int of_q6asm_parse_dai_data(struct device *dev,1228 struct q6asm_dai_data *pdata)1229{1230 struct snd_soc_dai_driver *dai_drv;1231 struct snd_soc_pcm_stream empty_stream;1232 struct device_node *node;1233 int ret, id, dir, idx = 0;1234 1235 1236 pdata->num_dais = of_get_child_count(dev->of_node);1237 if (!pdata->num_dais) {1238 dev_err(dev, "No dais found in DT\n");1239 return -EINVAL;1240 }1241 1242 pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv),1243 GFP_KERNEL);1244 if (!pdata->dais)1245 return -ENOMEM;1246 1247 memset(&empty_stream, 0, sizeof(empty_stream));1248 1249 for_each_child_of_node(dev->of_node, node) {1250 ret = of_property_read_u32(node, "reg", &id);1251 if (ret || id >= MAX_SESSIONS || id < 0) {1252 dev_err(dev, "valid dai id not found:%d\n", ret);1253 continue;1254 }1255 1256 dai_drv = &pdata->dais[idx++];1257 *dai_drv = q6asm_fe_dais_template[id];1258 1259 ret = of_property_read_u32(node, "direction", &dir);1260 if (ret)1261 continue;1262 1263 if (dir == Q6ASM_DAI_RX)1264 dai_drv->capture = empty_stream;1265 else if (dir == Q6ASM_DAI_TX)1266 dai_drv->playback = empty_stream;1267 1268 if (of_property_read_bool(node, "is-compress-dai"))1269 dai_drv->ops = &q6asm_dai_ops;1270 }1271 1272 return 0;1273}1274 1275static int q6asm_dai_probe(struct platform_device *pdev)1276{1277 struct device *dev = &pdev->dev;1278 struct device_node *node = dev->of_node;1279 struct of_phandle_args args;1280 struct q6asm_dai_data *pdata;1281 int rc;1282 1283 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);1284 if (!pdata)1285 return -ENOMEM;1286 1287 rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);1288 if (rc < 0)1289 pdata->sid = -1;1290 else1291 pdata->sid = args.args[0] & SID_MASK_DEFAULT;1292 1293 dev_set_drvdata(dev, pdata);1294 1295 rc = of_q6asm_parse_dai_data(dev, pdata);1296 if (rc)1297 return rc;1298 1299 return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,1300 pdata->dais, pdata->num_dais);1301}1302 1303#ifdef CONFIG_OF1304static const struct of_device_id q6asm_dai_device_id[] = {1305 { .compatible = "qcom,q6asm-dais" },1306 {},1307};1308MODULE_DEVICE_TABLE(of, q6asm_dai_device_id);1309#endif1310 1311static struct platform_driver q6asm_dai_platform_driver = {1312 .driver = {1313 .name = "q6asm-dai",1314 .of_match_table = of_match_ptr(q6asm_dai_device_id),1315 },1316 .probe = q6asm_dai_probe,1317};1318module_platform_driver(q6asm_dai_platform_driver);1319 1320MODULE_DESCRIPTION("Q6ASM dai driver");1321MODULE_LICENSE("GPL v2");1322