Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  sst_stream.c - Intel SST Driver for audio engine
0004  *
0005  *  Copyright (C) 2008-14 Intel Corp
0006  *  Authors:    Vinod Koul <vinod.koul@intel.com>
0007  *      Harsha Priya <priya.harsha@intel.com>
0008  *      Dharageswari R <dharageswari.r@intel.com>
0009  *      KP Jeeja <jeeja.kp@intel.com>
0010  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0011  *
0012  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0013  */
0014 #include <linux/pci.h>
0015 #include <linux/firmware.h>
0016 #include <linux/sched.h>
0017 #include <linux/delay.h>
0018 #include <linux/pm_runtime.h>
0019 #include <sound/core.h>
0020 #include <sound/pcm.h>
0021 #include <sound/soc.h>
0022 #include <sound/compress_driver.h>
0023 #include <asm/platform_sst_audio.h>
0024 #include "../sst-mfld-platform.h"
0025 #include "sst.h"
0026 
0027 int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
0028 {
0029     struct snd_pcm_params *pcm_params;
0030     struct snd_sst_params *str_params;
0031     struct snd_sst_tstamp fw_tstamp;
0032     struct stream_info *str_info;
0033     int i, num_ch, str_id;
0034 
0035     dev_dbg(sst_drv_ctx->dev, "Enter\n");
0036 
0037     str_params = (struct snd_sst_params *)params;
0038     str_id = str_params->stream_id;
0039     str_info = get_stream_info(sst_drv_ctx, str_id);
0040     if (!str_info)
0041         return -EINVAL;
0042 
0043     memset(&str_info->alloc_param, 0, sizeof(str_info->alloc_param));
0044     str_info->alloc_param.operation = str_params->ops;
0045     str_info->alloc_param.codec_type = str_params->codec;
0046     str_info->alloc_param.sg_count = str_params->aparams.sg_count;
0047     str_info->alloc_param.ring_buf_info[0].addr =
0048         str_params->aparams.ring_buf_info[0].addr;
0049     str_info->alloc_param.ring_buf_info[0].size =
0050         str_params->aparams.ring_buf_info[0].size;
0051     str_info->alloc_param.frag_size = str_params->aparams.frag_size;
0052 
0053     memcpy(&str_info->alloc_param.codec_params, &str_params->sparams,
0054             sizeof(struct snd_sst_stream_params));
0055 
0056     /*
0057      * fill channel map params for multichannel support.
0058      * Ideally channel map should be received from upper layers
0059      * for multichannel support.
0060      * Currently hardcoding as per FW reqm.
0061      */
0062     num_ch = sst_get_num_channel(str_params);
0063     pcm_params = &str_info->alloc_param.codec_params.uc.pcm_params;
0064     for (i = 0; i < 8; i++) {
0065         if (i < num_ch)
0066             pcm_params->channel_map[i] = i;
0067         else
0068             pcm_params->channel_map[i] = 0xff;
0069     }
0070 
0071     sst_drv_ctx->streams[str_id].status = STREAM_INIT;
0072     sst_drv_ctx->streams[str_id].prev = STREAM_UN_INIT;
0073     sst_drv_ctx->streams[str_id].pipe_id = str_params->device_type;
0074     sst_drv_ctx->streams[str_id].task_id = str_params->task;
0075     sst_drv_ctx->streams[str_id].num_ch = num_ch;
0076 
0077     if (sst_drv_ctx->info.lpe_viewpt_rqd)
0078         str_info->alloc_param.ts = sst_drv_ctx->info.mailbox_start +
0079             sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
0080     else
0081         str_info->alloc_param.ts = sst_drv_ctx->mailbox_add +
0082             sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
0083 
0084     dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
0085             str_info->alloc_param.ts);
0086     dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
0087             str_info->pipe_id, str_info->task_id);
0088 
0089     return sst_realloc_stream(sst_drv_ctx, str_id);
0090 }
0091 
0092 /**
0093  * sst_realloc_stream - Send msg for (re-)allocating a stream using the
0094  * @sst_drv_ctx: intel_sst_drv context pointer
0095  * @str_id: stream ID
0096  *
0097  * Send a msg for (re-)allocating a stream using the parameters previously
0098  * passed to sst_alloc_stream_mrfld() for the same stream ID.
0099  * Return: 0 or negative errno value.
0100  */
0101 int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0102 {
0103     struct snd_sst_alloc_response *response;
0104     struct stream_info *str_info;
0105     void *data = NULL;
0106     int ret;
0107 
0108     str_info = get_stream_info(sst_drv_ctx, str_id);
0109     if (!str_info)
0110         return -EINVAL;
0111 
0112     dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
0113         str_id, str_info->pipe_id);
0114 
0115     ret = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
0116             IPC_IA_ALLOC_STREAM_MRFLD, str_info->pipe_id,
0117             sizeof(str_info->alloc_param), &str_info->alloc_param,
0118             &data, true, true, false, true);
0119 
0120     if (ret < 0) {
0121         dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
0122         /* alloc failed, so reset the state to uninit */
0123         str_info->status = STREAM_UN_INIT;
0124         str_id = ret;
0125     } else if (data) {
0126         response = (struct snd_sst_alloc_response *)data;
0127         ret = response->str_type.result;
0128         if (!ret)
0129             goto out;
0130         dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
0131         if (ret == SST_ERR_STREAM_IN_USE) {
0132             dev_err(sst_drv_ctx->dev,
0133                 "FW not in clean state, send free for:%d\n", str_id);
0134             sst_free_stream(sst_drv_ctx, str_id);
0135         }
0136         str_id = -ret;
0137     }
0138 out:
0139     kfree(data);
0140     return str_id;
0141 }
0142 
0143 /**
0144  * sst_start_stream - Send msg for a starting stream
0145  * @sst_drv_ctx: intel_sst_drv context pointer
0146  * @str_id: stream ID
0147  *
0148  * This function is called by any function which wants to start
0149  * a stream.
0150  */
0151 int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0152 {
0153     int retval = 0;
0154     struct stream_info *str_info;
0155     u16 data = 0;
0156 
0157     dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
0158     str_info = get_stream_info(sst_drv_ctx, str_id);
0159     if (!str_info)
0160         return -EINVAL;
0161     if (str_info->status != STREAM_RUNNING)
0162         return -EBADRQC;
0163 
0164     retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
0165             IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
0166             sizeof(u16), &data, NULL, true, true, true, false);
0167 
0168     return retval;
0169 }
0170 
0171 int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
0172         struct snd_sst_bytes_v2 *bytes)
0173 {   struct ipc_post *msg = NULL;
0174     u32 length;
0175     int pvt_id, ret = 0;
0176     struct sst_block *block = NULL;
0177 
0178     dev_dbg(sst_drv_ctx->dev,
0179         "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
0180         bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
0181         bytes->pipe_id, bytes->len);
0182 
0183     if (sst_create_ipc_msg(&msg, true))
0184         return -ENOMEM;
0185 
0186     pvt_id = sst_assign_pvt_id(sst_drv_ctx);
0187     sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
0188             bytes->task_id, 1, pvt_id);
0189     msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
0190     length = bytes->len;
0191     msg->mrfld_header.p.header_low_payload = length;
0192     dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
0193     memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
0194     if (bytes->block) {
0195         block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
0196         if (block == NULL) {
0197             kfree(msg);
0198             ret = -ENOMEM;
0199             goto out;
0200         }
0201     }
0202 
0203     sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
0204     dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
0205             msg->mrfld_header.p.header_low_payload);
0206 
0207     if (bytes->block) {
0208         ret = sst_wait_timeout(sst_drv_ctx, block);
0209         if (ret) {
0210             dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
0211             sst_free_block(sst_drv_ctx, block);
0212             goto out;
0213         }
0214     }
0215     if (bytes->type == SND_SST_BYTES_GET) {
0216         /*
0217          * copy the reply and send back
0218          * we need to update only sz and payload
0219          */
0220         if (bytes->block) {
0221             unsigned char *r = block->data;
0222 
0223             dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
0224                     bytes->len);
0225             memcpy(bytes->bytes, r, bytes->len);
0226         }
0227     }
0228     if (bytes->block)
0229         sst_free_block(sst_drv_ctx, block);
0230 out:
0231     test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
0232     return ret;
0233 }
0234 
0235 /**
0236  * sst_pause_stream - Send msg for a pausing stream
0237  * @sst_drv_ctx: intel_sst_drv context pointer
0238  * @str_id: stream ID
0239  *
0240  * This function is called by any function which wants to pause
0241  * an already running stream.
0242  */
0243 int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0244 {
0245     int retval = 0;
0246     struct stream_info *str_info;
0247 
0248     dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
0249     str_info = get_stream_info(sst_drv_ctx, str_id);
0250     if (!str_info)
0251         return -EINVAL;
0252     if (str_info->status == STREAM_PAUSED)
0253         return 0;
0254     if (str_info->status == STREAM_RUNNING ||
0255         str_info->status == STREAM_INIT) {
0256         if (str_info->prev == STREAM_UN_INIT)
0257             return -EBADRQC;
0258 
0259         retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
0260                 IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
0261                 0, NULL, NULL, true, true, false, true);
0262 
0263         if (retval == 0) {
0264             str_info->prev = str_info->status;
0265             str_info->status = STREAM_PAUSED;
0266         } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
0267             retval = -EINVAL;
0268             mutex_lock(&sst_drv_ctx->sst_lock);
0269             sst_clean_stream(str_info);
0270             mutex_unlock(&sst_drv_ctx->sst_lock);
0271         }
0272     } else {
0273         retval = -EBADRQC;
0274         dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n");
0275     }
0276 
0277     return retval;
0278 }
0279 
0280 /**
0281  * sst_resume_stream - Send msg for resuming stream
0282  * @sst_drv_ctx: intel_sst_drv context pointer
0283  * @str_id: stream ID
0284  *
0285  * This function is called by any function which wants to resume
0286  * an already paused stream.
0287  */
0288 int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0289 {
0290     int retval = 0;
0291     struct stream_info *str_info;
0292 
0293     dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
0294     str_info = get_stream_info(sst_drv_ctx, str_id);
0295     if (!str_info)
0296         return -EINVAL;
0297     if (str_info->status == STREAM_RUNNING)
0298         return 0;
0299 
0300     if (str_info->resume_status == STREAM_PAUSED &&
0301         str_info->resume_prev == STREAM_RUNNING) {
0302         /*
0303          * Stream was running before suspend and re-created on resume,
0304          * start it to get back to running state.
0305          */
0306         dev_dbg(sst_drv_ctx->dev, "restart recreated stream after resume\n");
0307         str_info->status = STREAM_RUNNING;
0308         str_info->prev = STREAM_PAUSED;
0309         retval = sst_start_stream(sst_drv_ctx, str_id);
0310         str_info->resume_status = STREAM_UN_INIT;
0311     } else if (str_info->resume_status == STREAM_PAUSED &&
0312            str_info->resume_prev == STREAM_INIT) {
0313         /*
0314          * Stream was idle before suspend and re-created on resume,
0315          * keep it as is.
0316          */
0317         dev_dbg(sst_drv_ctx->dev, "leaving recreated stream idle after resume\n");
0318         str_info->status = STREAM_INIT;
0319         str_info->prev = STREAM_PAUSED;
0320         str_info->resume_status = STREAM_UN_INIT;
0321     } else if (str_info->status == STREAM_PAUSED) {
0322         retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
0323                 IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
0324                 str_info->pipe_id, 0, NULL, NULL,
0325                 true, true, false, true);
0326 
0327         if (!retval) {
0328             if (str_info->prev == STREAM_RUNNING)
0329                 str_info->status = STREAM_RUNNING;
0330             else
0331                 str_info->status = STREAM_INIT;
0332             str_info->prev = STREAM_PAUSED;
0333         } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
0334             retval = -EINVAL;
0335             mutex_lock(&sst_drv_ctx->sst_lock);
0336             sst_clean_stream(str_info);
0337             mutex_unlock(&sst_drv_ctx->sst_lock);
0338         }
0339     } else {
0340         retval = -EBADRQC;
0341         dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
0342     }
0343 
0344     return retval;
0345 }
0346 
0347 
0348 /**
0349  * sst_drop_stream - Send msg for stopping stream
0350  * @sst_drv_ctx: intel_sst_drv context pointer
0351  * @str_id: stream ID
0352  *
0353  * This function is called by any function which wants to stop
0354  * a stream.
0355  */
0356 int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0357 {
0358     int retval = 0;
0359     struct stream_info *str_info;
0360 
0361     dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
0362     str_info = get_stream_info(sst_drv_ctx, str_id);
0363     if (!str_info)
0364         return -EINVAL;
0365 
0366     if (str_info->status != STREAM_UN_INIT) {
0367         str_info->prev = STREAM_UN_INIT;
0368         str_info->status = STREAM_INIT;
0369         str_info->cumm_bytes = 0;
0370         retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
0371                 IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
0372                 str_info->pipe_id, 0, NULL, NULL,
0373                 true, true, true, false);
0374     } else {
0375         retval = -EBADRQC;
0376         dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
0377                 str_info->status);
0378     }
0379     return retval;
0380 }
0381 
0382 /**
0383  * sst_drain_stream - Send msg for draining stream
0384  * @sst_drv_ctx: intel_sst_drv context pointer
0385  * @str_id: stream ID
0386  * @partial_drain: boolean indicating if a gapless transition is taking place
0387  *
0388  * This function is called by any function which wants to drain
0389  * a stream.
0390  */
0391 int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
0392             int str_id, bool partial_drain)
0393 {
0394     int retval = 0;
0395     struct stream_info *str_info;
0396 
0397     dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
0398     str_info = get_stream_info(sst_drv_ctx, str_id);
0399     if (!str_info)
0400         return -EINVAL;
0401     if (str_info->status != STREAM_RUNNING &&
0402         str_info->status != STREAM_INIT &&
0403         str_info->status != STREAM_PAUSED) {
0404             dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
0405                        str_info->status);
0406             return -EBADRQC;
0407     }
0408 
0409     retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
0410             IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
0411             sizeof(u8), &partial_drain, NULL, true, true, false, false);
0412     /*
0413      * with new non blocked drain implementation in core we dont need to
0414      * wait for respsonse, and need to only invoke callback for drain
0415      * complete
0416      */
0417 
0418     return retval;
0419 }
0420 
0421 /**
0422  * sst_free_stream - Frees a stream
0423  * @sst_drv_ctx: intel_sst_drv context pointer
0424  * @str_id: stream ID
0425  *
0426  * This function is called by any function which wants to free
0427  * a stream.
0428  */
0429 int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
0430 {
0431     int retval = 0;
0432     struct stream_info *str_info;
0433 
0434     dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
0435 
0436     mutex_lock(&sst_drv_ctx->sst_lock);
0437     if (sst_drv_ctx->sst_state == SST_RESET) {
0438         mutex_unlock(&sst_drv_ctx->sst_lock);
0439         return -ENODEV;
0440     }
0441     mutex_unlock(&sst_drv_ctx->sst_lock);
0442     str_info = get_stream_info(sst_drv_ctx, str_id);
0443     if (!str_info)
0444         return -EINVAL;
0445 
0446     mutex_lock(&str_info->lock);
0447     if (str_info->status != STREAM_UN_INIT) {
0448         str_info->prev =  str_info->status;
0449         str_info->status = STREAM_UN_INIT;
0450         mutex_unlock(&str_info->lock);
0451 
0452         dev_dbg(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
0453                 str_id, str_info->pipe_id);
0454         retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
0455                 IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
0456                 NULL, NULL, true, true, false, true);
0457 
0458         dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
0459                 retval);
0460         mutex_lock(&sst_drv_ctx->sst_lock);
0461         sst_clean_stream(str_info);
0462         mutex_unlock(&sst_drv_ctx->sst_lock);
0463         dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
0464     } else {
0465         mutex_unlock(&str_info->lock);
0466         retval = -EBADRQC;
0467         dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
0468     }
0469 
0470     return retval;
0471 }