1200 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2020 Intel Corporation4//5// Author: Cezary Rojewski <cezary.rojewski@intel.com>6//7 8#include <linux/pm_runtime.h>9#include <sound/soc.h>10#include <sound/pcm_params.h>11#include <uapi/sound/tlv.h>12#include "core.h"13#include "messages.h"14 15struct catpt_stream_template {16 enum catpt_path_id path_id;17 enum catpt_stream_type type;18 u32 persistent_size;19 u8 num_entries;20 struct catpt_module_entry entries[];21};22 23static struct catpt_stream_template system_pb = {24 .path_id = CATPT_PATH_SSP0_OUT,25 .type = CATPT_STRM_TYPE_SYSTEM,26 .num_entries = 1,27 .entries = {{ CATPT_MODID_PCM_SYSTEM, 0 }},28};29 30static struct catpt_stream_template system_cp = {31 .path_id = CATPT_PATH_SSP0_IN,32 .type = CATPT_STRM_TYPE_CAPTURE,33 .num_entries = 1,34 .entries = {{ CATPT_MODID_PCM_CAPTURE, 0 }},35};36 37static struct catpt_stream_template offload_pb = {38 .path_id = CATPT_PATH_SSP0_OUT,39 .type = CATPT_STRM_TYPE_RENDER,40 .num_entries = 1,41 .entries = {{ CATPT_MODID_PCM, 0 }},42};43 44static struct catpt_stream_template loopback_cp = {45 .path_id = CATPT_PATH_SSP0_OUT,46 .type = CATPT_STRM_TYPE_LOOPBACK,47 .num_entries = 1,48 .entries = {{ CATPT_MODID_PCM_REFERENCE, 0 }},49};50 51static struct catpt_stream_template bluetooth_pb = {52 .path_id = CATPT_PATH_SSP1_OUT,53 .type = CATPT_STRM_TYPE_BLUETOOTH_RENDER,54 .num_entries = 1,55 .entries = {{ CATPT_MODID_BLUETOOTH_RENDER, 0 }},56};57 58static struct catpt_stream_template bluetooth_cp = {59 .path_id = CATPT_PATH_SSP1_IN,60 .type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE,61 .num_entries = 1,62 .entries = {{ CATPT_MODID_BLUETOOTH_CAPTURE, 0 }},63};64 65static struct catpt_stream_template *catpt_topology[] = {66 [CATPT_STRM_TYPE_RENDER] = &offload_pb,67 [CATPT_STRM_TYPE_SYSTEM] = &system_pb,68 [CATPT_STRM_TYPE_CAPTURE] = &system_cp,69 [CATPT_STRM_TYPE_LOOPBACK] = &loopback_cp,70 [CATPT_STRM_TYPE_BLUETOOTH_RENDER] = &bluetooth_pb,71 [CATPT_STRM_TYPE_BLUETOOTH_CAPTURE] = &bluetooth_cp,72};73 74static struct catpt_stream_template *75catpt_get_stream_template(struct snd_pcm_substream *substream)76{77 struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);78 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);79 enum catpt_stream_type type;80 81 type = cpu_dai->driver->id;82 83 /* account for capture in bidirectional dais */84 switch (type) {85 case CATPT_STRM_TYPE_SYSTEM:86 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)87 type = CATPT_STRM_TYPE_CAPTURE;88 break;89 case CATPT_STRM_TYPE_BLUETOOTH_RENDER:90 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)91 type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE;92 break;93 default:94 break;95 }96 97 return catpt_topology[type];98}99 100struct catpt_stream_runtime *101catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)102{103 struct catpt_stream_runtime *pos, *result = NULL;104 105 spin_lock(&cdev->list_lock);106 list_for_each_entry(pos, &cdev->stream_list, node) {107 if (pos->info.stream_hw_id == stream_hw_id) {108 result = pos;109 break;110 }111 }112 113 spin_unlock(&cdev->list_lock);114 return result;115}116 117static u32 catpt_stream_read_position(struct catpt_dev *cdev,118 struct catpt_stream_runtime *stream)119{120 u32 pos;121 122 memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,123 sizeof(pos));124 return pos;125}126 127static u32 catpt_stream_volume(struct catpt_dev *cdev,128 struct catpt_stream_runtime *stream, u32 channel)129{130 u32 volume, offset;131 132 if (channel >= CATPT_CHANNELS_MAX)133 channel = 0;134 135 offset = stream->info.volume_regaddr[channel];136 memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));137 return volume;138}139 140static u32 catpt_mixer_volume(struct catpt_dev *cdev,141 struct catpt_mixer_stream_info *info, u32 channel)142{143 u32 volume, offset;144 145 if (channel >= CATPT_CHANNELS_MAX)146 channel = 0;147 148 offset = info->volume_regaddr[channel];149 memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));150 return volume;151}152 153static void catpt_arrange_page_table(struct snd_pcm_substream *substream,154 struct snd_dma_buffer *pgtbl)155{156 struct snd_pcm_runtime *rtm = substream->runtime;157 struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream);158 int i, pages;159 160 pages = snd_sgbuf_aligned_pages(rtm->dma_bytes);161 162 for (i = 0; i < pages; i++) {163 u32 pfn, offset;164 u32 *page_table;165 166 pfn = PFN_DOWN(snd_sgbuf_get_addr(databuf, i * PAGE_SIZE));167 /* incrementing by 2 on even and 3 on odd */168 offset = ((i << 2) + i) >> 1;169 page_table = (u32 *)(pgtbl->area + offset);170 171 if (i & 1)172 *page_table |= (pfn << 4);173 else174 *page_table |= pfn;175 }176}177 178static u32 catpt_get_channel_map(enum catpt_channel_config config)179{180 switch (config) {181 case CATPT_CHANNEL_CONFIG_MONO:182 return GENMASK(31, 4) | CATPT_CHANNEL_CENTER;183 184 case CATPT_CHANNEL_CONFIG_STEREO:185 return GENMASK(31, 8) | CATPT_CHANNEL_LEFT186 | (CATPT_CHANNEL_RIGHT << 4);187 188 case CATPT_CHANNEL_CONFIG_2_POINT_1:189 return GENMASK(31, 12) | CATPT_CHANNEL_LEFT190 | (CATPT_CHANNEL_RIGHT << 4)191 | (CATPT_CHANNEL_LFE << 8);192 193 case CATPT_CHANNEL_CONFIG_3_POINT_0:194 return GENMASK(31, 12) | CATPT_CHANNEL_LEFT195 | (CATPT_CHANNEL_CENTER << 4)196 | (CATPT_CHANNEL_RIGHT << 8);197 198 case CATPT_CHANNEL_CONFIG_3_POINT_1:199 return GENMASK(31, 16) | CATPT_CHANNEL_LEFT200 | (CATPT_CHANNEL_CENTER << 4)201 | (CATPT_CHANNEL_RIGHT << 8)202 | (CATPT_CHANNEL_LFE << 12);203 204 case CATPT_CHANNEL_CONFIG_QUATRO:205 return GENMASK(31, 16) | CATPT_CHANNEL_LEFT206 | (CATPT_CHANNEL_RIGHT << 4)207 | (CATPT_CHANNEL_LEFT_SURROUND << 8)208 | (CATPT_CHANNEL_RIGHT_SURROUND << 12);209 210 case CATPT_CHANNEL_CONFIG_4_POINT_0:211 return GENMASK(31, 16) | CATPT_CHANNEL_LEFT212 | (CATPT_CHANNEL_CENTER << 4)213 | (CATPT_CHANNEL_RIGHT << 8)214 | (CATPT_CHANNEL_CENTER_SURROUND << 12);215 216 case CATPT_CHANNEL_CONFIG_5_POINT_0:217 return GENMASK(31, 20) | CATPT_CHANNEL_LEFT218 | (CATPT_CHANNEL_CENTER << 4)219 | (CATPT_CHANNEL_RIGHT << 8)220 | (CATPT_CHANNEL_LEFT_SURROUND << 12)221 | (CATPT_CHANNEL_RIGHT_SURROUND << 16);222 223 case CATPT_CHANNEL_CONFIG_5_POINT_1:224 return GENMASK(31, 24) | CATPT_CHANNEL_CENTER225 | (CATPT_CHANNEL_LEFT << 4)226 | (CATPT_CHANNEL_RIGHT << 8)227 | (CATPT_CHANNEL_LEFT_SURROUND << 12)228 | (CATPT_CHANNEL_RIGHT_SURROUND << 16)229 | (CATPT_CHANNEL_LFE << 20);230 231 case CATPT_CHANNEL_CONFIG_DUAL_MONO:232 return GENMASK(31, 8) | CATPT_CHANNEL_LEFT233 | (CATPT_CHANNEL_LEFT << 4);234 235 default:236 return U32_MAX;237 }238}239 240static enum catpt_channel_config catpt_get_channel_config(u32 num_channels)241{242 switch (num_channels) {243 case 6:244 return CATPT_CHANNEL_CONFIG_5_POINT_1;245 case 5:246 return CATPT_CHANNEL_CONFIG_5_POINT_0;247 case 4:248 return CATPT_CHANNEL_CONFIG_QUATRO;249 case 3:250 return CATPT_CHANNEL_CONFIG_2_POINT_1;251 case 1:252 return CATPT_CHANNEL_CONFIG_MONO;253 case 2:254 default:255 return CATPT_CHANNEL_CONFIG_STEREO;256 }257}258 259static int catpt_dai_startup(struct snd_pcm_substream *substream,260 struct snd_soc_dai *dai)261{262 struct catpt_stream_template *template;263 struct catpt_stream_runtime *stream;264 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);265 struct resource *res;266 int ret;267 268 template = catpt_get_stream_template(substream);269 270 stream = kzalloc(sizeof(*stream), GFP_KERNEL);271 if (!stream)272 return -ENOMEM;273 274 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE,275 &stream->pgtbl);276 if (ret)277 goto err_pgtbl;278 279 res = catpt_request_region(&cdev->dram, template->persistent_size);280 if (!res) {281 ret = -EBUSY;282 goto err_request;283 }284 285 catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);286 287 stream->template = template;288 stream->persistent = res;289 stream->substream = substream;290 INIT_LIST_HEAD(&stream->node);291 snd_soc_dai_set_dma_data(dai, substream, stream);292 293 spin_lock(&cdev->list_lock);294 list_add_tail(&stream->node, &cdev->stream_list);295 spin_unlock(&cdev->list_lock);296 297 return 0;298 299err_request:300 snd_dma_free_pages(&stream->pgtbl);301err_pgtbl:302 kfree(stream);303 return ret;304}305 306static void catpt_dai_shutdown(struct snd_pcm_substream *substream,307 struct snd_soc_dai *dai)308{309 struct catpt_stream_runtime *stream;310 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);311 312 stream = snd_soc_dai_get_dma_data(dai, substream);313 314 spin_lock(&cdev->list_lock);315 list_del(&stream->node);316 spin_unlock(&cdev->list_lock);317 318 release_resource(stream->persistent);319 kfree(stream->persistent);320 catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);321 322 snd_dma_free_pages(&stream->pgtbl);323 kfree(stream);324 snd_soc_dai_set_dma_data(dai, substream, NULL);325}326 327static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);328 329static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,330 struct catpt_stream_runtime *stream)331{332 struct snd_soc_component *component = dai->component;333 struct snd_kcontrol *pos;334 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);335 const char *name;336 int ret;337 u32 id = stream->info.stream_hw_id;338 339 /* only selected streams have individual controls */340 switch (id) {341 case CATPT_PIN_ID_OFFLOAD1:342 name = "Media0 Playback Volume";343 break;344 case CATPT_PIN_ID_OFFLOAD2:345 name = "Media1 Playback Volume";346 break;347 case CATPT_PIN_ID_CAPTURE1:348 name = "Mic Capture Volume";349 break;350 case CATPT_PIN_ID_REFERENCE:351 name = "Loopback Mute";352 break;353 default:354 return 0;355 }356 357 list_for_each_entry(pos, &component->card->snd_card->controls, list) {358 if (pos->private_data == component &&359 !strncmp(name, pos->id.name, sizeof(pos->id.name)))360 break;361 }362 if (list_entry_is_head(pos, &component->card->snd_card->controls, list))363 return -ENOENT;364 365 if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)366 return catpt_set_dspvol(cdev, id, (long *)pos->private_value);367 ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);368 if (ret)369 return CATPT_IPC_ERROR(ret);370 return 0;371}372 373static int catpt_dai_hw_params(struct snd_pcm_substream *substream,374 struct snd_pcm_hw_params *params,375 struct snd_soc_dai *dai)376{377 struct snd_pcm_runtime *rtm = substream->runtime;378 struct snd_dma_buffer *dmab;379 struct catpt_stream_runtime *stream;380 struct catpt_audio_format afmt;381 struct catpt_ring_info rinfo;382 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);383 int ret;384 385 stream = snd_soc_dai_get_dma_data(dai, substream);386 if (stream->allocated)387 return 0;388 389 memset(&afmt, 0, sizeof(afmt));390 afmt.sample_rate = params_rate(params);391 afmt.bit_depth = params_physical_width(params);392 afmt.valid_bit_depth = params_width(params);393 afmt.num_channels = params_channels(params);394 afmt.channel_config = catpt_get_channel_config(afmt.num_channels);395 afmt.channel_map = catpt_get_channel_map(afmt.channel_config);396 afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;397 398 dmab = snd_pcm_get_dma_buf(substream);399 catpt_arrange_page_table(substream, &stream->pgtbl);400 401 memset(&rinfo, 0, sizeof(rinfo));402 rinfo.page_table_addr = stream->pgtbl.addr;403 rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);404 rinfo.size = rtm->dma_bytes;405 rinfo.offset = 0;406 rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0));407 408 ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,409 stream->template->type,410 &afmt, &rinfo,411 stream->template->num_entries,412 stream->template->entries,413 stream->persistent,414 cdev->scratch,415 &stream->info);416 if (ret)417 return CATPT_IPC_ERROR(ret);418 419 ret = catpt_dai_apply_usettings(dai, stream);420 if (ret)421 return ret;422 423 stream->allocated = true;424 return 0;425}426 427static int catpt_dai_hw_free(struct snd_pcm_substream *substream,428 struct snd_soc_dai *dai)429{430 struct catpt_stream_runtime *stream;431 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);432 433 stream = snd_soc_dai_get_dma_data(dai, substream);434 if (!stream->allocated)435 return 0;436 437 catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);438 catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);439 440 stream->allocated = false;441 return 0;442}443 444static int catpt_dai_prepare(struct snd_pcm_substream *substream,445 struct snd_soc_dai *dai)446{447 struct catpt_stream_runtime *stream;448 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);449 int ret;450 451 stream = snd_soc_dai_get_dma_data(dai, substream);452 if (stream->prepared)453 return 0;454 455 ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);456 if (ret)457 return CATPT_IPC_ERROR(ret);458 459 ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);460 if (ret)461 return CATPT_IPC_ERROR(ret);462 463 stream->prepared = true;464 return 0;465}466 467static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,468 struct snd_soc_dai *dai)469{470 struct snd_pcm_runtime *runtime = substream->runtime;471 struct catpt_stream_runtime *stream;472 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);473 snd_pcm_uframes_t pos;474 int ret;475 476 stream = snd_soc_dai_get_dma_data(dai, substream);477 478 switch (cmd) {479 case SNDRV_PCM_TRIGGER_START:480 /* only offload is set_write_pos driven */481 if (stream->template->type != CATPT_STRM_TYPE_RENDER)482 goto resume_stream;483 484 pos = frames_to_bytes(runtime, runtime->start_threshold);485 /*486 * Dsp operates on buffer halves, thus max 2x set_write_pos487 * (entire buffer filled) prior to stream start.488 */489 ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,490 pos, false, false);491 if (ret)492 return CATPT_IPC_ERROR(ret);493 fallthrough;494 case SNDRV_PCM_TRIGGER_RESUME:495 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:496 resume_stream:497 catpt_dsp_update_lpclock(cdev);498 ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);499 if (ret)500 return CATPT_IPC_ERROR(ret);501 break;502 503 case SNDRV_PCM_TRIGGER_STOP:504 stream->prepared = false;505 fallthrough;506 case SNDRV_PCM_TRIGGER_SUSPEND:507 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:508 ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);509 catpt_dsp_update_lpclock(cdev);510 if (ret)511 return CATPT_IPC_ERROR(ret);512 break;513 514 default:515 break;516 }517 518 return 0;519}520 521void catpt_stream_update_position(struct catpt_dev *cdev,522 struct catpt_stream_runtime *stream,523 struct catpt_notify_position *pos)524{525 struct snd_pcm_substream *substream = stream->substream;526 struct snd_pcm_runtime *r = substream->runtime;527 snd_pcm_uframes_t dsppos, newpos;528 int ret;529 530 dsppos = bytes_to_frames(r, pos->stream_position);531 532 if (!stream->prepared)533 goto exit;534 /* only offload is set_write_pos driven */535 if (stream->template->type != CATPT_STRM_TYPE_RENDER)536 goto exit;537 538 if (dsppos >= r->buffer_size / 2)539 newpos = r->buffer_size / 2;540 else541 newpos = 0;542 /*543 * Dsp operates on buffer halves, thus on every notify position544 * (buffer half consumed) update wp to allow stream progression.545 */546 ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,547 frames_to_bytes(r, newpos),548 false, false);549 if (ret) {550 dev_err(cdev->dev, "update position for stream %d failed: %d\n",551 stream->info.stream_hw_id, ret);552 return;553 }554exit:555 snd_pcm_period_elapsed(substream);556}557 558/* 200 ms for 2 32-bit channels at 48kHz (native format) */559#define CATPT_BUFFER_MAX_SIZE 76800560#define CATPT_PCM_PERIODS_MAX 4561#define CATPT_PCM_PERIODS_MIN 2562 563static const struct snd_pcm_hardware catpt_pcm_hardware = {564 .info = SNDRV_PCM_INFO_MMAP |565 SNDRV_PCM_INFO_MMAP_VALID |566 SNDRV_PCM_INFO_INTERLEAVED |567 SNDRV_PCM_INFO_PAUSE |568 SNDRV_PCM_INFO_RESUME |569 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,570 .formats = SNDRV_PCM_FMTBIT_S16_LE |571 SNDRV_PCM_FMTBIT_S24_LE |572 SNDRV_PCM_FMTBIT_S32_LE,573 .period_bytes_min = PAGE_SIZE,574 .period_bytes_max = CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,575 .periods_min = CATPT_PCM_PERIODS_MIN,576 .periods_max = CATPT_PCM_PERIODS_MAX,577 .buffer_bytes_max = CATPT_BUFFER_MAX_SIZE,578};579 580static int catpt_component_pcm_construct(struct snd_soc_component *component,581 struct snd_soc_pcm_runtime *rtm)582{583 struct catpt_dev *cdev = dev_get_drvdata(component->dev);584 585 snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,586 cdev->dev,587 catpt_pcm_hardware.buffer_bytes_max,588 catpt_pcm_hardware.buffer_bytes_max);589 590 return 0;591}592 593static int catpt_component_open(struct snd_soc_component *component,594 struct snd_pcm_substream *substream)595{596 struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);597 598 if (!rtm->dai_link->no_pcm)599 snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);600 return 0;601}602 603static snd_pcm_uframes_t604catpt_component_pointer(struct snd_soc_component *component,605 struct snd_pcm_substream *substream)606{607 struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);608 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);609 struct catpt_stream_runtime *stream;610 struct catpt_dev *cdev = dev_get_drvdata(component->dev);611 u32 pos;612 613 if (rtm->dai_link->no_pcm)614 return 0;615 616 stream = snd_soc_dai_get_dma_data(cpu_dai, substream);617 pos = catpt_stream_read_position(cdev, stream);618 619 return bytes_to_frames(substream->runtime, pos);620}621 622static const struct snd_soc_dai_ops catpt_fe_dai_ops = {623 .startup = catpt_dai_startup,624 .shutdown = catpt_dai_shutdown,625 .hw_params = catpt_dai_hw_params,626 .hw_free = catpt_dai_hw_free,627 .prepare = catpt_dai_prepare,628 .trigger = catpt_dai_trigger,629};630 631static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,632 struct snd_soc_dai *dai)633{634 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);635 struct catpt_ssp_device_format devfmt;636 struct catpt_dev *cdev = dev_get_drvdata(dai->dev);637 int ret;638 639 devfmt.iface = dai->driver->id;640 devfmt.channels = codec_dai->driver->capture.channels_max;641 642 switch (devfmt.iface) {643 case CATPT_SSP_IFACE_0:644 devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ;645 646 switch (devfmt.channels) {647 case 4:648 devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER;649 devfmt.clock_divider = 4;650 break;651 case 2:652 default:653 devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER;654 devfmt.clock_divider = 9;655 break;656 }657 break;658 659 case CATPT_SSP_IFACE_1:660 devfmt.mclk = CATPT_MCLK_OFF;661 devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER;662 devfmt.clock_divider = 0;663 break;664 }665 666 /* see if this is a new configuration */667 if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))668 return 0;669 670 ret = pm_runtime_resume_and_get(cdev->dev);671 if (ret < 0 && ret != -EACCES)672 return ret;673 674 ret = catpt_ipc_set_device_format(cdev, &devfmt);675 676 pm_runtime_mark_last_busy(cdev->dev);677 pm_runtime_put_autosuspend(cdev->dev);678 679 if (ret)680 return CATPT_IPC_ERROR(ret);681 682 /* store device format set for given SSP */683 memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));684 return 0;685}686 687static const struct snd_soc_dai_ops catpt_dai_ops = {688 .pcm_new = catpt_dai_pcm_new,689};690 691static struct snd_soc_dai_driver dai_drivers[] = {692/* FE DAIs */693{694 .name = "System Pin",695 .id = CATPT_STRM_TYPE_SYSTEM,696 .ops = &catpt_fe_dai_ops,697 .playback = {698 .stream_name = "System Playback",699 .channels_min = 2,700 .channels_max = 2,701 .rates = SNDRV_PCM_RATE_48000,702 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,703 },704 .capture = {705 .stream_name = "Analog Capture",706 .channels_min = 2,707 .channels_max = 4,708 .rates = SNDRV_PCM_RATE_48000,709 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,710 },711},712{713 .name = "Offload0 Pin",714 .id = CATPT_STRM_TYPE_RENDER,715 .ops = &catpt_fe_dai_ops,716 .playback = {717 .stream_name = "Offload0 Playback",718 .channels_min = 2,719 .channels_max = 2,720 .rates = SNDRV_PCM_RATE_8000_192000,721 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,722 },723},724{725 .name = "Offload1 Pin",726 .id = CATPT_STRM_TYPE_RENDER,727 .ops = &catpt_fe_dai_ops,728 .playback = {729 .stream_name = "Offload1 Playback",730 .channels_min = 2,731 .channels_max = 2,732 .rates = SNDRV_PCM_RATE_8000_192000,733 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,734 },735},736{737 .name = "Loopback Pin",738 .id = CATPT_STRM_TYPE_LOOPBACK,739 .ops = &catpt_fe_dai_ops,740 .capture = {741 .stream_name = "Loopback Capture",742 .channels_min = 2,743 .channels_max = 2,744 .rates = SNDRV_PCM_RATE_48000,745 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,746 },747},748{749 .name = "Bluetooth Pin",750 .id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,751 .ops = &catpt_fe_dai_ops,752 .playback = {753 .stream_name = "Bluetooth Playback",754 .channels_min = 1,755 .channels_max = 1,756 .rates = SNDRV_PCM_RATE_8000,757 .formats = SNDRV_PCM_FMTBIT_S16_LE,758 },759 .capture = {760 .stream_name = "Bluetooth Capture",761 .channels_min = 1,762 .channels_max = 1,763 .rates = SNDRV_PCM_RATE_8000,764 .formats = SNDRV_PCM_FMTBIT_S16_LE,765 },766},767/* BE DAIs */768{769 .name = "ssp0-port",770 .id = CATPT_SSP_IFACE_0,771 .playback = {772 .channels_min = 1,773 .channels_max = 8,774 },775 .capture = {776 .channels_min = 1,777 .channels_max = 8,778 },779 .ops = &catpt_dai_ops,780},781{782 .name = "ssp1-port",783 .id = CATPT_SSP_IFACE_1,784 .playback = {785 .channels_min = 1,786 .channels_max = 8,787 },788 .capture = {789 .channels_min = 1,790 .channels_max = 8,791 },792 .ops = &catpt_dai_ops,793},794};795 796#define DSP_VOLUME_MAX S32_MAX /* 0db */797#define DSP_VOLUME_STEP_MAX 30798 799static u32 ctlvol_to_dspvol(u32 value)800{801 if (value > DSP_VOLUME_STEP_MAX)802 value = 0;803 return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);804}805 806static u32 dspvol_to_ctlvol(u32 volume)807{808 if (volume > DSP_VOLUME_MAX)809 return DSP_VOLUME_STEP_MAX;810 return volume ? __fls(volume) : 0;811}812 813static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)814{815 u32 dspvol;816 int ret, i;817 818 for (i = 1; i < CATPT_CHANNELS_MAX; i++)819 if (ctlvol[i] != ctlvol[0])820 break;821 822 if (i == CATPT_CHANNELS_MAX) {823 dspvol = ctlvol_to_dspvol(ctlvol[0]);824 825 ret = catpt_ipc_set_volume(cdev, stream_id,826 CATPT_ALL_CHANNELS_MASK, dspvol,827 0, CATPT_AUDIO_CURVE_NONE);828 } else {829 for (i = 0; i < CATPT_CHANNELS_MAX; i++) {830 dspvol = ctlvol_to_dspvol(ctlvol[i]);831 832 ret = catpt_ipc_set_volume(cdev, stream_id,833 i, dspvol,834 0, CATPT_AUDIO_CURVE_NONE);835 if (ret)836 break;837 }838 }839 840 if (ret)841 return CATPT_IPC_ERROR(ret);842 return 0;843}844 845static int catpt_volume_info(struct snd_kcontrol *kcontrol,846 struct snd_ctl_elem_info *uinfo)847{848 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;849 uinfo->count = CATPT_CHANNELS_MAX;850 uinfo->value.integer.min = 0;851 uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;852 return 0;853}854 855static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,856 struct snd_ctl_elem_value *ucontrol)857{858 struct snd_soc_component *component =859 snd_soc_kcontrol_component(kcontrol);860 struct catpt_dev *cdev = dev_get_drvdata(component->dev);861 u32 dspvol;862 int ret;863 int i;864 865 ret = pm_runtime_resume_and_get(cdev->dev);866 if (ret < 0 && ret != -EACCES)867 return ret;868 869 for (i = 0; i < CATPT_CHANNELS_MAX; i++) {870 dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);871 ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);872 }873 874 pm_runtime_mark_last_busy(cdev->dev);875 pm_runtime_put_autosuspend(cdev->dev);876 877 return 0;878}879 880static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,881 struct snd_ctl_elem_value *ucontrol)882{883 struct snd_soc_component *component =884 snd_soc_kcontrol_component(kcontrol);885 struct catpt_dev *cdev = dev_get_drvdata(component->dev);886 int ret;887 888 ret = pm_runtime_resume_and_get(cdev->dev);889 if (ret < 0 && ret != -EACCES)890 return ret;891 892 ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,893 ucontrol->value.integer.value);894 895 pm_runtime_mark_last_busy(cdev->dev);896 pm_runtime_put_autosuspend(cdev->dev);897 898 return ret;899}900 901static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,902 struct snd_ctl_elem_value *ucontrol,903 enum catpt_pin_id pin_id)904{905 struct snd_soc_component *component =906 snd_soc_kcontrol_component(kcontrol);907 struct catpt_stream_runtime *stream;908 struct catpt_dev *cdev = dev_get_drvdata(component->dev);909 long *ctlvol = (long *)kcontrol->private_value;910 u32 dspvol;911 int ret;912 int i;913 914 stream = catpt_stream_find(cdev, pin_id);915 if (!stream) {916 for (i = 0; i < CATPT_CHANNELS_MAX; i++)917 ucontrol->value.integer.value[i] = ctlvol[i];918 return 0;919 }920 921 ret = pm_runtime_resume_and_get(cdev->dev);922 if (ret < 0 && ret != -EACCES)923 return ret;924 925 for (i = 0; i < CATPT_CHANNELS_MAX; i++) {926 dspvol = catpt_stream_volume(cdev, stream, i);927 ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);928 }929 930 pm_runtime_mark_last_busy(cdev->dev);931 pm_runtime_put_autosuspend(cdev->dev);932 933 return 0;934}935 936static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,937 struct snd_ctl_elem_value *ucontrol,938 enum catpt_pin_id pin_id)939{940 struct snd_soc_component *component =941 snd_soc_kcontrol_component(kcontrol);942 struct catpt_stream_runtime *stream;943 struct catpt_dev *cdev = dev_get_drvdata(component->dev);944 long *ctlvol = (long *)kcontrol->private_value;945 int ret, i;946 947 stream = catpt_stream_find(cdev, pin_id);948 if (!stream) {949 for (i = 0; i < CATPT_CHANNELS_MAX; i++)950 ctlvol[i] = ucontrol->value.integer.value[i];951 return 0;952 }953 954 ret = pm_runtime_resume_and_get(cdev->dev);955 if (ret < 0 && ret != -EACCES)956 return ret;957 958 ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,959 ucontrol->value.integer.value);960 961 pm_runtime_mark_last_busy(cdev->dev);962 pm_runtime_put_autosuspend(cdev->dev);963 964 if (ret)965 return ret;966 967 for (i = 0; i < CATPT_CHANNELS_MAX; i++)968 ctlvol[i] = ucontrol->value.integer.value[i];969 return 0;970}971 972static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,973 struct snd_ctl_elem_value *uctl)974{975 return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);976}977 978static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,979 struct snd_ctl_elem_value *uctl)980{981 return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);982}983 984static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,985 struct snd_ctl_elem_value *uctl)986{987 return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);988}989 990static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,991 struct snd_ctl_elem_value *uctl)992{993 return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);994}995 996static int catpt_capture_volume_get(struct snd_kcontrol *kctl,997 struct snd_ctl_elem_value *uctl)998{999 return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);1000}1001 1002static int catpt_capture_volume_put(struct snd_kcontrol *kctl,1003 struct snd_ctl_elem_value *uctl)1004{1005 return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);1006}1007 1008static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,1009 struct snd_ctl_elem_value *ucontrol)1010{1011 ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;1012 return 0;1013}1014 1015static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,1016 struct snd_ctl_elem_value *ucontrol)1017{1018 struct snd_soc_component *component =1019 snd_soc_kcontrol_component(kcontrol);1020 struct catpt_stream_runtime *stream;1021 struct catpt_dev *cdev = dev_get_drvdata(component->dev);1022 bool mute;1023 int ret;1024 1025 mute = (bool)ucontrol->value.integer.value[0];1026 stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);1027 if (!stream) {1028 *(bool *)kcontrol->private_value = mute;1029 return 0;1030 }1031 1032 ret = pm_runtime_resume_and_get(cdev->dev);1033 if (ret < 0 && ret != -EACCES)1034 return ret;1035 1036 ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);1037 1038 pm_runtime_mark_last_busy(cdev->dev);1039 pm_runtime_put_autosuspend(cdev->dev);1040 1041 if (ret)1042 return CATPT_IPC_ERROR(ret);1043 1044 *(bool *)kcontrol->private_value = mute;1045 return 0;1046}1047 1048static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,1049 struct snd_ctl_elem_value *ucontrol)1050{1051 return 0;1052}1053 1054static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,1055 struct snd_ctl_elem_value *ucontrol)1056{1057 return 0;1058}1059 1060static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,1061 unsigned int __user *bytes,1062 unsigned int size)1063{1064 return 0;1065}1066 1067static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,1068 const unsigned int __user *bytes,1069 unsigned int size)1070{1071 return 0;1072}1073 1074static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);1075 1076#define CATPT_VOLUME_CTL(kname, sname) \1077{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \1078 .name = (kname), \1079 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \1080 SNDRV_CTL_ELEM_ACCESS_READWRITE, \1081 .info = catpt_volume_info, \1082 .get = catpt_##sname##_volume_get, \1083 .put = catpt_##sname##_volume_put, \1084 .tlv.p = catpt_volume_tlv, \1085 .private_value = (unsigned long) \1086 &(long[CATPT_CHANNELS_MAX]) {0} }1087 1088static const struct snd_kcontrol_new component_kcontrols[] = {1089/* Master volume (mixer stream) */1090CATPT_VOLUME_CTL("Master Playback Volume", mixer),1091/* Individual volume controls for offload and capture */1092CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),1093CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),1094CATPT_VOLUME_CTL("Mic Capture Volume", capture),1095SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},1096 catpt_loopback_switch_get, catpt_loopback_switch_put),1097/* Enable or disable WAVES module */1098SOC_SINGLE_BOOL_EXT("Waves Switch", 0,1099 catpt_waves_switch_get, catpt_waves_switch_put),1100/* WAVES module parameter control */1101SND_SOC_BYTES_TLV("Waves Set Param", 128,1102 catpt_waves_param_get, catpt_waves_param_put),1103};1104 1105static const struct snd_soc_dapm_widget component_widgets[] = {1106 SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),1107 SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),1108 SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),1109 SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),1110 1111 SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),1112};1113 1114static const struct snd_soc_dapm_route component_routes[] = {1115 {"Playback VMixer", NULL, "System Playback"},1116 {"Playback VMixer", NULL, "Offload0 Playback"},1117 {"Playback VMixer", NULL, "Offload1 Playback"},1118 1119 {"SSP0 CODEC OUT", NULL, "Playback VMixer"},1120 1121 {"Analog Capture", NULL, "SSP0 CODEC IN"},1122 {"Loopback Capture", NULL, "SSP0 CODEC IN"},1123 1124 {"SSP1 BT OUT", NULL, "Bluetooth Playback"},1125 {"Bluetooth Capture", NULL, "SSP1 BT IN"},1126};1127 1128static const struct snd_soc_component_driver catpt_comp_driver = {1129 .name = "catpt-platform",1130 1131 .pcm_construct = catpt_component_pcm_construct,1132 .open = catpt_component_open,1133 .pointer = catpt_component_pointer,1134 1135 .controls = component_kcontrols,1136 .num_controls = ARRAY_SIZE(component_kcontrols),1137 .dapm_widgets = component_widgets,1138 .num_dapm_widgets = ARRAY_SIZE(component_widgets),1139 .dapm_routes = component_routes,1140 .num_dapm_routes = ARRAY_SIZE(component_routes),1141};1142 1143int catpt_arm_stream_templates(struct catpt_dev *cdev)1144{1145 struct resource *res;1146 u32 scratch_size = 0;1147 int i, j;1148 1149 for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {1150 struct catpt_stream_template *template;1151 struct catpt_module_entry *entry;1152 struct catpt_module_type *type;1153 1154 template = catpt_topology[i];1155 template->persistent_size = 0;1156 1157 for (j = 0; j < template->num_entries; j++) {1158 entry = &template->entries[j];1159 type = &cdev->modules[entry->module_id];1160 1161 if (!type->loaded)1162 return -ENOENT;1163 1164 entry->entry_point = type->entry_point;1165 template->persistent_size += type->persistent_size;1166 if (type->scratch_size > scratch_size)1167 scratch_size = type->scratch_size;1168 }1169 }1170 1171 if (scratch_size) {1172 /* allocate single scratch area for all modules */1173 res = catpt_request_region(&cdev->dram, scratch_size);1174 if (!res)1175 return -EBUSY;1176 cdev->scratch = res;1177 }1178 1179 return 0;1180}1181 1182int catpt_register_plat_component(struct catpt_dev *cdev)1183{1184 struct snd_soc_component *component;1185 int ret;1186 1187 component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);1188 if (!component)1189 return -ENOMEM;1190 1191 ret = snd_soc_component_initialize(component, &catpt_comp_driver,1192 cdev->dev);1193 if (ret)1194 return ret;1195 1196 component->name = catpt_comp_driver.name;1197 return snd_soc_add_component(component, dai_drivers,1198 ARRAY_SIZE(dai_drivers));1199}1200