Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2020 Intel Corporation. All rights reserved.
0004 //
0005 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
0006 //
0007 
0008 #include <linux/pm_runtime.h>
0009 #include <sound/soc.h>
0010 #include <sound/pcm_params.h>
0011 #include <uapi/sound/tlv.h>
0012 #include "core.h"
0013 #include "messages.h"
0014 
0015 struct catpt_stream_template {
0016     enum catpt_path_id path_id;
0017     enum catpt_stream_type type;
0018     u32 persistent_size;
0019     u8 num_entries;
0020     struct catpt_module_entry entries[];
0021 };
0022 
0023 static struct catpt_stream_template system_pb = {
0024     .path_id = CATPT_PATH_SSP0_OUT,
0025     .type = CATPT_STRM_TYPE_SYSTEM,
0026     .num_entries = 1,
0027     .entries = {{ CATPT_MODID_PCM_SYSTEM, 0 }},
0028 };
0029 
0030 static struct catpt_stream_template system_cp = {
0031     .path_id = CATPT_PATH_SSP0_IN,
0032     .type = CATPT_STRM_TYPE_CAPTURE,
0033     .num_entries = 1,
0034     .entries = {{ CATPT_MODID_PCM_CAPTURE, 0 }},
0035 };
0036 
0037 static struct catpt_stream_template offload_pb = {
0038     .path_id = CATPT_PATH_SSP0_OUT,
0039     .type = CATPT_STRM_TYPE_RENDER,
0040     .num_entries = 1,
0041     .entries = {{ CATPT_MODID_PCM, 0 }},
0042 };
0043 
0044 static struct catpt_stream_template loopback_cp = {
0045     .path_id = CATPT_PATH_SSP0_OUT,
0046     .type = CATPT_STRM_TYPE_LOOPBACK,
0047     .num_entries = 1,
0048     .entries = {{ CATPT_MODID_PCM_REFERENCE, 0 }},
0049 };
0050 
0051 static struct catpt_stream_template bluetooth_pb = {
0052     .path_id = CATPT_PATH_SSP1_OUT,
0053     .type = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
0054     .num_entries = 1,
0055     .entries = {{ CATPT_MODID_BLUETOOTH_RENDER, 0 }},
0056 };
0057 
0058 static struct catpt_stream_template bluetooth_cp = {
0059     .path_id = CATPT_PATH_SSP1_IN,
0060     .type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE,
0061     .num_entries = 1,
0062     .entries = {{ CATPT_MODID_BLUETOOTH_CAPTURE, 0 }},
0063 };
0064 
0065 static struct catpt_stream_template *catpt_topology[] = {
0066     [CATPT_STRM_TYPE_RENDER]        = &offload_pb,
0067     [CATPT_STRM_TYPE_SYSTEM]        = &system_pb,
0068     [CATPT_STRM_TYPE_CAPTURE]       = &system_cp,
0069     [CATPT_STRM_TYPE_LOOPBACK]      = &loopback_cp,
0070     [CATPT_STRM_TYPE_BLUETOOTH_RENDER]  = &bluetooth_pb,
0071     [CATPT_STRM_TYPE_BLUETOOTH_CAPTURE] = &bluetooth_cp,
0072 };
0073 
0074 static struct catpt_stream_template *
0075 catpt_get_stream_template(struct snd_pcm_substream *substream)
0076 {
0077     struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream);
0078     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0);
0079     enum catpt_stream_type type;
0080 
0081     type = cpu_dai->driver->id;
0082 
0083     /* account for capture in bidirectional dais */
0084     switch (type) {
0085     case CATPT_STRM_TYPE_SYSTEM:
0086         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0087             type = CATPT_STRM_TYPE_CAPTURE;
0088         break;
0089     case CATPT_STRM_TYPE_BLUETOOTH_RENDER:
0090         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0091             type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE;
0092         break;
0093     default:
0094         break;
0095     }
0096 
0097     return catpt_topology[type];
0098 }
0099 
0100 struct catpt_stream_runtime *
0101 catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
0102 {
0103     struct catpt_stream_runtime *pos, *result = NULL;
0104 
0105     spin_lock(&cdev->list_lock);
0106     list_for_each_entry(pos, &cdev->stream_list, node) {
0107         if (pos->info.stream_hw_id == stream_hw_id) {
0108             result = pos;
0109             break;
0110         }
0111     }
0112 
0113     spin_unlock(&cdev->list_lock);
0114     return result;
0115 }
0116 
0117 static u32 catpt_stream_read_position(struct catpt_dev *cdev,
0118                       struct catpt_stream_runtime *stream)
0119 {
0120     u32 pos;
0121 
0122     memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,
0123               sizeof(pos));
0124     return pos;
0125 }
0126 
0127 static u32 catpt_stream_volume(struct catpt_dev *cdev,
0128                    struct catpt_stream_runtime *stream, u32 channel)
0129 {
0130     u32 volume, offset;
0131 
0132     if (channel >= CATPT_CHANNELS_MAX)
0133         channel = 0;
0134 
0135     offset = stream->info.volume_regaddr[channel];
0136     memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));
0137     return volume;
0138 }
0139 
0140 static u32 catpt_mixer_volume(struct catpt_dev *cdev,
0141                   struct catpt_mixer_stream_info *info, u32 channel)
0142 {
0143     u32 volume, offset;
0144 
0145     if (channel >= CATPT_CHANNELS_MAX)
0146         channel = 0;
0147 
0148     offset = info->volume_regaddr[channel];
0149     memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));
0150     return volume;
0151 }
0152 
0153 static void catpt_arrange_page_table(struct snd_pcm_substream *substream,
0154                      struct snd_dma_buffer *pgtbl)
0155 {
0156     struct snd_pcm_runtime *rtm = substream->runtime;
0157     struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream);
0158     int i, pages;
0159 
0160     pages = snd_sgbuf_aligned_pages(rtm->dma_bytes);
0161 
0162     for (i = 0; i < pages; i++) {
0163         u32 pfn, offset;
0164         u32 *page_table;
0165 
0166         pfn = PFN_DOWN(snd_sgbuf_get_addr(databuf, i * PAGE_SIZE));
0167         /* incrementing by 2 on even and 3 on odd */
0168         offset = ((i << 2) + i) >> 1;
0169         page_table = (u32 *)(pgtbl->area + offset);
0170 
0171         if (i & 1)
0172             *page_table |= (pfn << 4);
0173         else
0174             *page_table |= pfn;
0175     }
0176 }
0177 
0178 static u32 catpt_get_channel_map(enum catpt_channel_config config)
0179 {
0180     switch (config) {
0181     case CATPT_CHANNEL_CONFIG_MONO:
0182         return GENMASK(31, 4) | CATPT_CHANNEL_CENTER;
0183 
0184     case CATPT_CHANNEL_CONFIG_STEREO:
0185         return GENMASK(31, 8) | CATPT_CHANNEL_LEFT
0186                       | (CATPT_CHANNEL_RIGHT << 4);
0187 
0188     case CATPT_CHANNEL_CONFIG_2_POINT_1:
0189         return GENMASK(31, 12) | CATPT_CHANNEL_LEFT
0190                        | (CATPT_CHANNEL_RIGHT << 4)
0191                        | (CATPT_CHANNEL_LFE << 8);
0192 
0193     case CATPT_CHANNEL_CONFIG_3_POINT_0:
0194         return GENMASK(31, 12) | CATPT_CHANNEL_LEFT
0195                        | (CATPT_CHANNEL_CENTER << 4)
0196                        | (CATPT_CHANNEL_RIGHT << 8);
0197 
0198     case CATPT_CHANNEL_CONFIG_3_POINT_1:
0199         return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
0200                        | (CATPT_CHANNEL_CENTER << 4)
0201                        | (CATPT_CHANNEL_RIGHT << 8)
0202                        | (CATPT_CHANNEL_LFE << 12);
0203 
0204     case CATPT_CHANNEL_CONFIG_QUATRO:
0205         return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
0206                        | (CATPT_CHANNEL_RIGHT << 4)
0207                        | (CATPT_CHANNEL_LEFT_SURROUND << 8)
0208                        | (CATPT_CHANNEL_RIGHT_SURROUND << 12);
0209 
0210     case CATPT_CHANNEL_CONFIG_4_POINT_0:
0211         return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
0212                        | (CATPT_CHANNEL_CENTER << 4)
0213                        | (CATPT_CHANNEL_RIGHT << 8)
0214                        | (CATPT_CHANNEL_CENTER_SURROUND << 12);
0215 
0216     case CATPT_CHANNEL_CONFIG_5_POINT_0:
0217         return GENMASK(31, 20) | CATPT_CHANNEL_LEFT
0218                        | (CATPT_CHANNEL_CENTER << 4)
0219                        | (CATPT_CHANNEL_RIGHT << 8)
0220                        | (CATPT_CHANNEL_LEFT_SURROUND << 12)
0221                        | (CATPT_CHANNEL_RIGHT_SURROUND << 16);
0222 
0223     case CATPT_CHANNEL_CONFIG_5_POINT_1:
0224         return GENMASK(31, 24) | CATPT_CHANNEL_CENTER
0225                        | (CATPT_CHANNEL_LEFT << 4)
0226                        | (CATPT_CHANNEL_RIGHT << 8)
0227                        | (CATPT_CHANNEL_LEFT_SURROUND << 12)
0228                        | (CATPT_CHANNEL_RIGHT_SURROUND << 16)
0229                        | (CATPT_CHANNEL_LFE << 20);
0230 
0231     case CATPT_CHANNEL_CONFIG_DUAL_MONO:
0232         return GENMASK(31, 8) | CATPT_CHANNEL_LEFT
0233                       | (CATPT_CHANNEL_LEFT << 4);
0234 
0235     default:
0236         return U32_MAX;
0237     }
0238 }
0239 
0240 static enum catpt_channel_config catpt_get_channel_config(u32 num_channels)
0241 {
0242     switch (num_channels) {
0243     case 6:
0244         return CATPT_CHANNEL_CONFIG_5_POINT_1;
0245     case 5:
0246         return CATPT_CHANNEL_CONFIG_5_POINT_0;
0247     case 4:
0248         return CATPT_CHANNEL_CONFIG_QUATRO;
0249     case 3:
0250         return CATPT_CHANNEL_CONFIG_2_POINT_1;
0251     case 1:
0252         return CATPT_CHANNEL_CONFIG_MONO;
0253     case 2:
0254     default:
0255         return CATPT_CHANNEL_CONFIG_STEREO;
0256     }
0257 }
0258 
0259 static int catpt_dai_startup(struct snd_pcm_substream *substream,
0260                  struct snd_soc_dai *dai)
0261 {
0262     struct catpt_stream_template *template;
0263     struct catpt_stream_runtime *stream;
0264     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0265     struct resource *res;
0266     int ret;
0267 
0268     template = catpt_get_stream_template(substream);
0269 
0270     stream = kzalloc(sizeof(*stream), GFP_KERNEL);
0271     if (!stream)
0272         return -ENOMEM;
0273 
0274     ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE,
0275                   &stream->pgtbl);
0276     if (ret)
0277         goto err_pgtbl;
0278 
0279     res = catpt_request_region(&cdev->dram, template->persistent_size);
0280     if (!res) {
0281         ret = -EBUSY;
0282         goto err_request;
0283     }
0284 
0285     catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
0286 
0287     stream->template = template;
0288     stream->persistent = res;
0289     stream->substream = substream;
0290     INIT_LIST_HEAD(&stream->node);
0291     snd_soc_dai_set_dma_data(dai, substream, stream);
0292 
0293     spin_lock(&cdev->list_lock);
0294     list_add_tail(&stream->node, &cdev->stream_list);
0295     spin_unlock(&cdev->list_lock);
0296 
0297     return 0;
0298 
0299 err_request:
0300     snd_dma_free_pages(&stream->pgtbl);
0301 err_pgtbl:
0302     kfree(stream);
0303     return ret;
0304 }
0305 
0306 static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
0307                    struct snd_soc_dai *dai)
0308 {
0309     struct catpt_stream_runtime *stream;
0310     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0311 
0312     stream = snd_soc_dai_get_dma_data(dai, substream);
0313 
0314     spin_lock(&cdev->list_lock);
0315     list_del(&stream->node);
0316     spin_unlock(&cdev->list_lock);
0317 
0318     release_resource(stream->persistent);
0319     kfree(stream->persistent);
0320     catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
0321 
0322     snd_dma_free_pages(&stream->pgtbl);
0323     kfree(stream);
0324     snd_soc_dai_set_dma_data(dai, substream, NULL);
0325 }
0326 
0327 static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
0328 
0329 static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
0330                      struct catpt_stream_runtime *stream)
0331 {
0332     struct snd_soc_component *component = dai->component;
0333     struct snd_kcontrol *pos;
0334     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0335     const char *name;
0336     int ret;
0337     u32 id = stream->info.stream_hw_id;
0338 
0339     /* only selected streams have individual controls */
0340     switch (id) {
0341     case CATPT_PIN_ID_OFFLOAD1:
0342         name = "Media0 Playback Volume";
0343         break;
0344     case CATPT_PIN_ID_OFFLOAD2:
0345         name = "Media1 Playback Volume";
0346         break;
0347     case CATPT_PIN_ID_CAPTURE1:
0348         name = "Mic Capture Volume";
0349         break;
0350     case CATPT_PIN_ID_REFERENCE:
0351         name = "Loopback Mute";
0352         break;
0353     default:
0354         return 0;
0355     }
0356 
0357     list_for_each_entry(pos, &component->card->snd_card->controls, list) {
0358         if (pos->private_data == component &&
0359             !strncmp(name, pos->id.name, sizeof(pos->id.name)))
0360             break;
0361     }
0362     if (list_entry_is_head(pos, &component->card->snd_card->controls, list))
0363         return -ENOENT;
0364 
0365     if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
0366         return catpt_set_dspvol(cdev, id, (long *)pos->private_value);
0367     ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);
0368     if (ret)
0369         return CATPT_IPC_ERROR(ret);
0370     return 0;
0371 }
0372 
0373 static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
0374                    struct snd_pcm_hw_params *params,
0375                    struct snd_soc_dai *dai)
0376 {
0377     struct snd_pcm_runtime *rtm = substream->runtime;
0378     struct snd_dma_buffer *dmab;
0379     struct catpt_stream_runtime *stream;
0380     struct catpt_audio_format afmt;
0381     struct catpt_ring_info rinfo;
0382     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0383     int ret;
0384 
0385     stream = snd_soc_dai_get_dma_data(dai, substream);
0386     if (stream->allocated)
0387         return 0;
0388 
0389     memset(&afmt, 0, sizeof(afmt));
0390     afmt.sample_rate = params_rate(params);
0391     afmt.bit_depth = params_physical_width(params);
0392     afmt.valid_bit_depth = params_width(params);
0393     afmt.num_channels = params_channels(params);
0394     afmt.channel_config = catpt_get_channel_config(afmt.num_channels);
0395     afmt.channel_map = catpt_get_channel_map(afmt.channel_config);
0396     afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;
0397 
0398     dmab = snd_pcm_get_dma_buf(substream);
0399     catpt_arrange_page_table(substream, &stream->pgtbl);
0400 
0401     memset(&rinfo, 0, sizeof(rinfo));
0402     rinfo.page_table_addr = stream->pgtbl.addr;
0403     rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);
0404     rinfo.size = rtm->dma_bytes;
0405     rinfo.offset = 0;
0406     rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0));
0407 
0408     ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,
0409                      stream->template->type,
0410                      &afmt, &rinfo,
0411                      stream->template->num_entries,
0412                      stream->template->entries,
0413                      stream->persistent,
0414                      cdev->scratch,
0415                      &stream->info);
0416     if (ret)
0417         return CATPT_IPC_ERROR(ret);
0418 
0419     ret = catpt_dai_apply_usettings(dai, stream);
0420     if (ret)
0421         return ret;
0422 
0423     stream->allocated = true;
0424     return 0;
0425 }
0426 
0427 static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
0428                  struct snd_soc_dai *dai)
0429 {
0430     struct catpt_stream_runtime *stream;
0431     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0432 
0433     stream = snd_soc_dai_get_dma_data(dai, substream);
0434     if (!stream->allocated)
0435         return 0;
0436 
0437     catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
0438     catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
0439 
0440     stream->allocated = false;
0441     return 0;
0442 }
0443 
0444 static int catpt_dai_prepare(struct snd_pcm_substream *substream,
0445                  struct snd_soc_dai *dai)
0446 {
0447     struct catpt_stream_runtime *stream;
0448     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0449     int ret;
0450 
0451     stream = snd_soc_dai_get_dma_data(dai, substream);
0452     if (stream->prepared)
0453         return 0;
0454 
0455     ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
0456     if (ret)
0457         return CATPT_IPC_ERROR(ret);
0458 
0459     ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
0460     if (ret)
0461         return CATPT_IPC_ERROR(ret);
0462 
0463     stream->prepared = true;
0464     return 0;
0465 }
0466 
0467 static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
0468                  struct snd_soc_dai *dai)
0469 {
0470     struct snd_pcm_runtime *runtime = substream->runtime;
0471     struct catpt_stream_runtime *stream;
0472     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0473     snd_pcm_uframes_t pos;
0474     int ret;
0475 
0476     stream = snd_soc_dai_get_dma_data(dai, substream);
0477 
0478     switch (cmd) {
0479     case SNDRV_PCM_TRIGGER_START:
0480         /* only offload is set_write_pos driven */
0481         if (stream->template->type != CATPT_STRM_TYPE_RENDER)
0482             goto resume_stream;
0483 
0484         pos = frames_to_bytes(runtime, runtime->start_threshold);
0485         /*
0486          * Dsp operates on buffer halves, thus max 2x set_write_pos
0487          * (entire buffer filled) prior to stream start.
0488          */
0489         ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
0490                           pos, false, false);
0491         if (ret)
0492             return CATPT_IPC_ERROR(ret);
0493         fallthrough;
0494     case SNDRV_PCM_TRIGGER_RESUME:
0495     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0496     resume_stream:
0497         catpt_dsp_update_lpclock(cdev);
0498         ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
0499         if (ret)
0500             return CATPT_IPC_ERROR(ret);
0501         break;
0502 
0503     case SNDRV_PCM_TRIGGER_STOP:
0504         stream->prepared = false;
0505         fallthrough;
0506     case SNDRV_PCM_TRIGGER_SUSPEND:
0507     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0508         ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
0509         catpt_dsp_update_lpclock(cdev);
0510         if (ret)
0511             return CATPT_IPC_ERROR(ret);
0512         break;
0513 
0514     default:
0515         break;
0516     }
0517 
0518     return 0;
0519 }
0520 
0521 void catpt_stream_update_position(struct catpt_dev *cdev,
0522                   struct catpt_stream_runtime *stream,
0523                   struct catpt_notify_position *pos)
0524 {
0525     struct snd_pcm_substream *substream = stream->substream;
0526     struct snd_pcm_runtime *r = substream->runtime;
0527     snd_pcm_uframes_t dsppos, newpos;
0528     int ret;
0529 
0530     dsppos = bytes_to_frames(r, pos->stream_position);
0531 
0532     if (!stream->prepared)
0533         goto exit;
0534     /* only offload is set_write_pos driven */
0535     if (stream->template->type != CATPT_STRM_TYPE_RENDER)
0536         goto exit;
0537 
0538     if (dsppos >= r->buffer_size / 2)
0539         newpos = r->buffer_size / 2;
0540     else
0541         newpos = 0;
0542     /*
0543      * Dsp operates on buffer halves, thus on every notify position
0544      * (buffer half consumed) update wp to allow stream progression.
0545      */
0546     ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
0547                       frames_to_bytes(r, newpos),
0548                       false, false);
0549     if (ret) {
0550         dev_err(cdev->dev, "update position for stream %d failed: %d\n",
0551             stream->info.stream_hw_id, ret);
0552         return;
0553     }
0554 exit:
0555     snd_pcm_period_elapsed(substream);
0556 }
0557 
0558 /* 200 ms for 2 32-bit channels at 48kHz (native format) */
0559 #define CATPT_BUFFER_MAX_SIZE   76800
0560 #define CATPT_PCM_PERIODS_MAX   4
0561 #define CATPT_PCM_PERIODS_MIN   2
0562 
0563 static const struct snd_pcm_hardware catpt_pcm_hardware = {
0564     .info           = SNDRV_PCM_INFO_MMAP |
0565                   SNDRV_PCM_INFO_MMAP_VALID |
0566                   SNDRV_PCM_INFO_INTERLEAVED |
0567                   SNDRV_PCM_INFO_PAUSE |
0568                   SNDRV_PCM_INFO_RESUME |
0569                   SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
0570     .formats        = SNDRV_PCM_FMTBIT_S16_LE |
0571                   SNDRV_PCM_FMTBIT_S24_LE |
0572                   SNDRV_PCM_FMTBIT_S32_LE,
0573     .period_bytes_min   = PAGE_SIZE,
0574     .period_bytes_max   = CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,
0575     .periods_min        = CATPT_PCM_PERIODS_MIN,
0576     .periods_max        = CATPT_PCM_PERIODS_MAX,
0577     .buffer_bytes_max   = CATPT_BUFFER_MAX_SIZE,
0578 };
0579 
0580 static int catpt_component_pcm_construct(struct snd_soc_component *component,
0581                      struct snd_soc_pcm_runtime *rtm)
0582 {
0583     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0584 
0585     snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,
0586                        cdev->dev,
0587                        catpt_pcm_hardware.buffer_bytes_max,
0588                        catpt_pcm_hardware.buffer_bytes_max);
0589 
0590     return 0;
0591 }
0592 
0593 static int catpt_component_open(struct snd_soc_component *component,
0594                 struct snd_pcm_substream *substream)
0595 {
0596     struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream);
0597 
0598     if (!rtm->dai_link->no_pcm)
0599         snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);
0600     return 0;
0601 }
0602 
0603 static snd_pcm_uframes_t
0604 catpt_component_pointer(struct snd_soc_component *component,
0605             struct snd_pcm_substream *substream)
0606 {
0607     struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream);
0608     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0);
0609     struct catpt_stream_runtime *stream;
0610     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0611     u32 pos;
0612 
0613     if (rtm->dai_link->no_pcm)
0614         return 0;
0615 
0616     stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
0617     pos = catpt_stream_read_position(cdev, stream);
0618 
0619     return bytes_to_frames(substream->runtime, pos);
0620 }
0621 
0622 static const struct snd_soc_dai_ops catpt_fe_dai_ops = {
0623     .startup = catpt_dai_startup,
0624     .shutdown = catpt_dai_shutdown,
0625     .hw_params = catpt_dai_hw_params,
0626     .hw_free = catpt_dai_hw_free,
0627     .prepare = catpt_dai_prepare,
0628     .trigger = catpt_dai_trigger,
0629 };
0630 
0631 static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
0632                  struct snd_soc_dai *dai)
0633 {
0634     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0);
0635     struct catpt_ssp_device_format devfmt;
0636     struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
0637     int ret;
0638 
0639     devfmt.iface = dai->driver->id;
0640     devfmt.channels = codec_dai->driver->capture.channels_max;
0641 
0642     switch (devfmt.iface) {
0643     case CATPT_SSP_IFACE_0:
0644         devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ;
0645 
0646         switch (devfmt.channels) {
0647         case 4:
0648             devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER;
0649             devfmt.clock_divider = 4;
0650             break;
0651         case 2:
0652         default:
0653             devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER;
0654             devfmt.clock_divider = 9;
0655             break;
0656         }
0657         break;
0658 
0659     case CATPT_SSP_IFACE_1:
0660         devfmt.mclk = CATPT_MCLK_OFF;
0661         devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER;
0662         devfmt.clock_divider = 0;
0663         break;
0664     }
0665 
0666     /* see if this is a new configuration */
0667     if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))
0668         return 0;
0669 
0670     ret = pm_runtime_resume_and_get(cdev->dev);
0671     if (ret < 0 && ret != -EACCES)
0672         return ret;
0673 
0674     ret = catpt_ipc_set_device_format(cdev, &devfmt);
0675 
0676     pm_runtime_mark_last_busy(cdev->dev);
0677     pm_runtime_put_autosuspend(cdev->dev);
0678 
0679     if (ret)
0680         return CATPT_IPC_ERROR(ret);
0681 
0682     /* store device format set for given SSP */
0683     memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
0684     return 0;
0685 }
0686 
0687 static struct snd_soc_dai_driver dai_drivers[] = {
0688 /* FE DAIs */
0689 {
0690     .name  = "System Pin",
0691     .id = CATPT_STRM_TYPE_SYSTEM,
0692     .ops = &catpt_fe_dai_ops,
0693     .playback = {
0694         .stream_name = "System Playback",
0695         .channels_min = 2,
0696         .channels_max = 2,
0697         .rates = SNDRV_PCM_RATE_48000,
0698         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
0699     },
0700     .capture = {
0701         .stream_name = "Analog Capture",
0702         .channels_min = 2,
0703         .channels_max = 4,
0704         .rates = SNDRV_PCM_RATE_48000,
0705         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
0706     },
0707 },
0708 {
0709     .name  = "Offload0 Pin",
0710     .id = CATPT_STRM_TYPE_RENDER,
0711     .ops = &catpt_fe_dai_ops,
0712     .playback = {
0713         .stream_name = "Offload0 Playback",
0714         .channels_min = 2,
0715         .channels_max = 2,
0716         .rates = SNDRV_PCM_RATE_8000_192000,
0717         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
0718     },
0719 },
0720 {
0721     .name  = "Offload1 Pin",
0722     .id = CATPT_STRM_TYPE_RENDER,
0723     .ops = &catpt_fe_dai_ops,
0724     .playback = {
0725         .stream_name = "Offload1 Playback",
0726         .channels_min = 2,
0727         .channels_max = 2,
0728         .rates = SNDRV_PCM_RATE_8000_192000,
0729         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
0730     },
0731 },
0732 {
0733     .name  = "Loopback Pin",
0734     .id = CATPT_STRM_TYPE_LOOPBACK,
0735     .ops = &catpt_fe_dai_ops,
0736     .capture = {
0737         .stream_name = "Loopback Capture",
0738         .channels_min = 2,
0739         .channels_max = 2,
0740         .rates = SNDRV_PCM_RATE_48000,
0741         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
0742     },
0743 },
0744 {
0745     .name  = "Bluetooth Pin",
0746     .id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
0747     .ops = &catpt_fe_dai_ops,
0748     .playback = {
0749         .stream_name = "Bluetooth Playback",
0750         .channels_min = 1,
0751         .channels_max = 1,
0752         .rates = SNDRV_PCM_RATE_8000,
0753         .formats = SNDRV_PCM_FMTBIT_S16_LE,
0754     },
0755     .capture = {
0756         .stream_name = "Bluetooth Capture",
0757         .channels_min = 1,
0758         .channels_max = 1,
0759         .rates = SNDRV_PCM_RATE_8000,
0760         .formats = SNDRV_PCM_FMTBIT_S16_LE,
0761     },
0762 },
0763 /* BE DAIs */
0764 {
0765     .name = "ssp0-port",
0766     .id = CATPT_SSP_IFACE_0,
0767     .pcm_new = catpt_dai_pcm_new,
0768     .playback = {
0769         .channels_min = 1,
0770         .channels_max = 8,
0771     },
0772     .capture = {
0773         .channels_min = 1,
0774         .channels_max = 8,
0775     },
0776 },
0777 {
0778     .name = "ssp1-port",
0779     .id = CATPT_SSP_IFACE_1,
0780     .pcm_new = catpt_dai_pcm_new,
0781     .playback = {
0782         .channels_min = 1,
0783         .channels_max = 8,
0784     },
0785     .capture = {
0786         .channels_min = 1,
0787         .channels_max = 8,
0788     },
0789 },
0790 };
0791 
0792 #define DSP_VOLUME_MAX      S32_MAX /* 0db */
0793 #define DSP_VOLUME_STEP_MAX 30
0794 
0795 static u32 ctlvol_to_dspvol(u32 value)
0796 {
0797     if (value > DSP_VOLUME_STEP_MAX)
0798         value = 0;
0799     return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);
0800 }
0801 
0802 static u32 dspvol_to_ctlvol(u32 volume)
0803 {
0804     if (volume > DSP_VOLUME_MAX)
0805         return DSP_VOLUME_STEP_MAX;
0806     return volume ? __fls(volume) : 0;
0807 }
0808 
0809 static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
0810 {
0811     u32 dspvol;
0812     int ret, i;
0813 
0814     for (i = 1; i < CATPT_CHANNELS_MAX; i++)
0815         if (ctlvol[i] != ctlvol[0])
0816             break;
0817 
0818     if (i == CATPT_CHANNELS_MAX) {
0819         dspvol = ctlvol_to_dspvol(ctlvol[0]);
0820 
0821         ret = catpt_ipc_set_volume(cdev, stream_id,
0822                        CATPT_ALL_CHANNELS_MASK, dspvol,
0823                        0, CATPT_AUDIO_CURVE_NONE);
0824     } else {
0825         for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
0826             dspvol = ctlvol_to_dspvol(ctlvol[i]);
0827 
0828             ret = catpt_ipc_set_volume(cdev, stream_id,
0829                            i, dspvol,
0830                            0, CATPT_AUDIO_CURVE_NONE);
0831             if (ret)
0832                 break;
0833         }
0834     }
0835 
0836     if (ret)
0837         return CATPT_IPC_ERROR(ret);
0838     return 0;
0839 }
0840 
0841 static int catpt_volume_info(struct snd_kcontrol *kcontrol,
0842                  struct snd_ctl_elem_info *uinfo)
0843 {
0844     uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
0845     uinfo->count = CATPT_CHANNELS_MAX;
0846     uinfo->value.integer.min = 0;
0847     uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;
0848     return 0;
0849 }
0850 
0851 static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
0852                   struct snd_ctl_elem_value *ucontrol)
0853 {
0854     struct snd_soc_component *component =
0855         snd_soc_kcontrol_component(kcontrol);
0856     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0857     u32 dspvol;
0858     int ret;
0859     int i;
0860 
0861     ret = pm_runtime_resume_and_get(cdev->dev);
0862     if (ret < 0 && ret != -EACCES)
0863         return ret;
0864 
0865     for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
0866         dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
0867         ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
0868     }
0869 
0870     pm_runtime_mark_last_busy(cdev->dev);
0871     pm_runtime_put_autosuspend(cdev->dev);
0872 
0873     return 0;
0874 }
0875 
0876 static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
0877                   struct snd_ctl_elem_value *ucontrol)
0878 {
0879     struct snd_soc_component *component =
0880         snd_soc_kcontrol_component(kcontrol);
0881     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0882     int ret;
0883 
0884     ret = pm_runtime_resume_and_get(cdev->dev);
0885     if (ret < 0 && ret != -EACCES)
0886         return ret;
0887 
0888     ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
0889                    ucontrol->value.integer.value);
0890 
0891     pm_runtime_mark_last_busy(cdev->dev);
0892     pm_runtime_put_autosuspend(cdev->dev);
0893 
0894     return ret;
0895 }
0896 
0897 static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
0898                    struct snd_ctl_elem_value *ucontrol,
0899                    enum catpt_pin_id pin_id)
0900 {
0901     struct snd_soc_component *component =
0902         snd_soc_kcontrol_component(kcontrol);
0903     struct catpt_stream_runtime *stream;
0904     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0905     long *ctlvol = (long *)kcontrol->private_value;
0906     u32 dspvol;
0907     int ret;
0908     int i;
0909 
0910     stream = catpt_stream_find(cdev, pin_id);
0911     if (!stream) {
0912         for (i = 0; i < CATPT_CHANNELS_MAX; i++)
0913             ucontrol->value.integer.value[i] = ctlvol[i];
0914         return 0;
0915     }
0916 
0917     ret = pm_runtime_resume_and_get(cdev->dev);
0918     if (ret < 0 && ret != -EACCES)
0919         return ret;
0920 
0921     for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
0922         dspvol = catpt_stream_volume(cdev, stream, i);
0923         ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
0924     }
0925 
0926     pm_runtime_mark_last_busy(cdev->dev);
0927     pm_runtime_put_autosuspend(cdev->dev);
0928 
0929     return 0;
0930 }
0931 
0932 static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
0933                    struct snd_ctl_elem_value *ucontrol,
0934                    enum catpt_pin_id pin_id)
0935 {
0936     struct snd_soc_component *component =
0937         snd_soc_kcontrol_component(kcontrol);
0938     struct catpt_stream_runtime *stream;
0939     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
0940     long *ctlvol = (long *)kcontrol->private_value;
0941     int ret, i;
0942 
0943     stream = catpt_stream_find(cdev, pin_id);
0944     if (!stream) {
0945         for (i = 0; i < CATPT_CHANNELS_MAX; i++)
0946             ctlvol[i] = ucontrol->value.integer.value[i];
0947         return 0;
0948     }
0949 
0950     ret = pm_runtime_resume_and_get(cdev->dev);
0951     if (ret < 0 && ret != -EACCES)
0952         return ret;
0953 
0954     ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
0955                    ucontrol->value.integer.value);
0956 
0957     pm_runtime_mark_last_busy(cdev->dev);
0958     pm_runtime_put_autosuspend(cdev->dev);
0959 
0960     if (ret)
0961         return ret;
0962 
0963     for (i = 0; i < CATPT_CHANNELS_MAX; i++)
0964         ctlvol[i] = ucontrol->value.integer.value[i];
0965     return 0;
0966 }
0967 
0968 static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,
0969                      struct snd_ctl_elem_value *uctl)
0970 {
0971     return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
0972 }
0973 
0974 static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,
0975                      struct snd_ctl_elem_value *uctl)
0976 {
0977     return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
0978 }
0979 
0980 static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,
0981                      struct snd_ctl_elem_value *uctl)
0982 {
0983     return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
0984 }
0985 
0986 static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,
0987                      struct snd_ctl_elem_value *uctl)
0988 {
0989     return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
0990 }
0991 
0992 static int catpt_capture_volume_get(struct snd_kcontrol *kctl,
0993                     struct snd_ctl_elem_value *uctl)
0994 {
0995     return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
0996 }
0997 
0998 static int catpt_capture_volume_put(struct snd_kcontrol *kctl,
0999                     struct snd_ctl_elem_value *uctl)
1000 {
1001     return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
1002 }
1003 
1004 static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
1005                      struct snd_ctl_elem_value *ucontrol)
1006 {
1007     ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
1008     return 0;
1009 }
1010 
1011 static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
1012                      struct snd_ctl_elem_value *ucontrol)
1013 {
1014     struct snd_soc_component *component =
1015         snd_soc_kcontrol_component(kcontrol);
1016     struct catpt_stream_runtime *stream;
1017     struct catpt_dev *cdev = dev_get_drvdata(component->dev);
1018     bool mute;
1019     int ret;
1020 
1021     mute = (bool)ucontrol->value.integer.value[0];
1022     stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
1023     if (!stream) {
1024         *(bool *)kcontrol->private_value = mute;
1025         return 0;
1026     }
1027 
1028     ret = pm_runtime_resume_and_get(cdev->dev);
1029     if (ret < 0 && ret != -EACCES)
1030         return ret;
1031 
1032     ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
1033 
1034     pm_runtime_mark_last_busy(cdev->dev);
1035     pm_runtime_put_autosuspend(cdev->dev);
1036 
1037     if (ret)
1038         return CATPT_IPC_ERROR(ret);
1039 
1040     *(bool *)kcontrol->private_value = mute;
1041     return 0;
1042 }
1043 
1044 static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
1045                   struct snd_ctl_elem_value *ucontrol)
1046 {
1047     return 0;
1048 }
1049 
1050 static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
1051                   struct snd_ctl_elem_value *ucontrol)
1052 {
1053     return 0;
1054 }
1055 
1056 static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
1057                  unsigned int __user *bytes,
1058                  unsigned int size)
1059 {
1060     return 0;
1061 }
1062 
1063 static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
1064                  const unsigned int __user *bytes,
1065                  unsigned int size)
1066 {
1067     return 0;
1068 }
1069 
1070 static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
1071 
1072 #define CATPT_VOLUME_CTL(kname, sname) \
1073 {   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1074     .name = (kname), \
1075     .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
1076           SNDRV_CTL_ELEM_ACCESS_READWRITE, \
1077     .info = catpt_volume_info, \
1078     .get = catpt_##sname##_volume_get, \
1079     .put = catpt_##sname##_volume_put, \
1080     .tlv.p = catpt_volume_tlv, \
1081     .private_value = (unsigned long) \
1082         &(long[CATPT_CHANNELS_MAX]) {0} }
1083 
1084 static const struct snd_kcontrol_new component_kcontrols[] = {
1085 /* Master volume (mixer stream) */
1086 CATPT_VOLUME_CTL("Master Playback Volume", mixer),
1087 /* Individual volume controls for offload and capture */
1088 CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),
1089 CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),
1090 CATPT_VOLUME_CTL("Mic Capture Volume", capture),
1091 SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},
1092             catpt_loopback_switch_get, catpt_loopback_switch_put),
1093 /* Enable or disable WAVES module */
1094 SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
1095             catpt_waves_switch_get, catpt_waves_switch_put),
1096 /* WAVES module parameter control */
1097 SND_SOC_BYTES_TLV("Waves Set Param", 128,
1098           catpt_waves_param_get, catpt_waves_param_put),
1099 };
1100 
1101 static const struct snd_soc_dapm_widget component_widgets[] = {
1102     SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1103     SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1104     SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1105     SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1106 
1107     SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
1108 };
1109 
1110 static const struct snd_soc_dapm_route component_routes[] = {
1111     {"Playback VMixer", NULL, "System Playback"},
1112     {"Playback VMixer", NULL, "Offload0 Playback"},
1113     {"Playback VMixer", NULL, "Offload1 Playback"},
1114 
1115     {"SSP0 CODEC OUT", NULL, "Playback VMixer"},
1116 
1117     {"Analog Capture", NULL, "SSP0 CODEC IN"},
1118     {"Loopback Capture", NULL, "SSP0 CODEC IN"},
1119 
1120     {"SSP1 BT OUT", NULL, "Bluetooth Playback"},
1121     {"Bluetooth Capture", NULL, "SSP1 BT IN"},
1122 };
1123 
1124 static const struct snd_soc_component_driver catpt_comp_driver = {
1125     .name = "catpt-platform",
1126 
1127     .pcm_construct = catpt_component_pcm_construct,
1128     .open = catpt_component_open,
1129     .pointer = catpt_component_pointer,
1130 
1131     .controls = component_kcontrols,
1132     .num_controls = ARRAY_SIZE(component_kcontrols),
1133     .dapm_widgets = component_widgets,
1134     .num_dapm_widgets = ARRAY_SIZE(component_widgets),
1135     .dapm_routes = component_routes,
1136     .num_dapm_routes = ARRAY_SIZE(component_routes),
1137 };
1138 
1139 int catpt_arm_stream_templates(struct catpt_dev *cdev)
1140 {
1141     struct resource *res;
1142     u32 scratch_size = 0;
1143     int i, j;
1144 
1145     for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
1146         struct catpt_stream_template *template;
1147         struct catpt_module_entry *entry;
1148         struct catpt_module_type *type;
1149 
1150         template = catpt_topology[i];
1151         template->persistent_size = 0;
1152 
1153         for (j = 0; j < template->num_entries; j++) {
1154             entry = &template->entries[j];
1155             type = &cdev->modules[entry->module_id];
1156 
1157             if (!type->loaded)
1158                 return -ENOENT;
1159 
1160             entry->entry_point = type->entry_point;
1161             template->persistent_size += type->persistent_size;
1162             if (type->scratch_size > scratch_size)
1163                 scratch_size = type->scratch_size;
1164         }
1165     }
1166 
1167     if (scratch_size) {
1168         /* allocate single scratch area for all modules */
1169         res = catpt_request_region(&cdev->dram, scratch_size);
1170         if (!res)
1171             return -EBUSY;
1172         cdev->scratch = res;
1173     }
1174 
1175     return 0;
1176 }
1177 
1178 int catpt_register_plat_component(struct catpt_dev *cdev)
1179 {
1180     struct snd_soc_component *component;
1181     int ret;
1182 
1183     component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
1184     if (!component)
1185         return -ENOMEM;
1186 
1187     ret = snd_soc_component_initialize(component, &catpt_comp_driver,
1188                        cdev->dev);
1189     if (ret)
1190         return ret;
1191 
1192     component->name = catpt_comp_driver.name;
1193     return snd_soc_add_component(component, dai_drivers,
1194                      ARRAY_SIZE(dai_drivers));
1195 }