472 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sst_stream.c - Intel SST Driver for audio engine4 *5 * Copyright (C) 2008-14 Intel Corp6 * Authors: Vinod Koul <vinod.koul@intel.com>7 * Harsha Priya <priya.harsha@intel.com>8 * Dharageswari R <dharageswari.r@intel.com>9 * KP Jeeja <jeeja.kp@intel.com>10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~11 *12 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~13 */14#include <linux/pci.h>15#include <linux/firmware.h>16#include <linux/sched.h>17#include <linux/delay.h>18#include <sound/core.h>19#include <sound/pcm.h>20#include <sound/soc.h>21#include <sound/compress_driver.h>22#include <asm/platform_sst_audio.h>23#include "../sst-mfld-platform.h"24#include "sst.h"25 26int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)27{28 struct snd_pcm_params *pcm_params;29 struct snd_sst_params *str_params;30 struct snd_sst_tstamp fw_tstamp;31 struct stream_info *str_info;32 int i, num_ch, str_id;33 34 dev_dbg(sst_drv_ctx->dev, "Enter\n");35 36 str_params = (struct snd_sst_params *)params;37 str_id = str_params->stream_id;38 str_info = get_stream_info(sst_drv_ctx, str_id);39 if (!str_info)40 return -EINVAL;41 42 memset(&str_info->alloc_param, 0, sizeof(str_info->alloc_param));43 str_info->alloc_param.operation = str_params->ops;44 str_info->alloc_param.codec_type = str_params->codec;45 str_info->alloc_param.sg_count = str_params->aparams.sg_count;46 str_info->alloc_param.ring_buf_info[0].addr =47 str_params->aparams.ring_buf_info[0].addr;48 str_info->alloc_param.ring_buf_info[0].size =49 str_params->aparams.ring_buf_info[0].size;50 str_info->alloc_param.frag_size = str_params->aparams.frag_size;51 52 memcpy(&str_info->alloc_param.codec_params, &str_params->sparams,53 sizeof(struct snd_sst_stream_params));54 55 /*56 * fill channel map params for multichannel support.57 * Ideally channel map should be received from upper layers58 * for multichannel support.59 * Currently hardcoding as per FW reqm.60 */61 num_ch = sst_get_num_channel(str_params);62 pcm_params = &str_info->alloc_param.codec_params.uc.pcm_params;63 for (i = 0; i < 8; i++) {64 if (i < num_ch)65 pcm_params->channel_map[i] = i;66 else67 pcm_params->channel_map[i] = 0xff;68 }69 70 sst_drv_ctx->streams[str_id].status = STREAM_INIT;71 sst_drv_ctx->streams[str_id].prev = STREAM_UN_INIT;72 sst_drv_ctx->streams[str_id].pipe_id = str_params->device_type;73 sst_drv_ctx->streams[str_id].task_id = str_params->task;74 sst_drv_ctx->streams[str_id].num_ch = num_ch;75 76 if (sst_drv_ctx->info.lpe_viewpt_rqd)77 str_info->alloc_param.ts = sst_drv_ctx->info.mailbox_start +78 sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));79 else80 str_info->alloc_param.ts = sst_drv_ctx->mailbox_add +81 sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));82 83 dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",84 str_info->alloc_param.ts);85 dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",86 str_info->pipe_id, str_info->task_id);87 88 return sst_realloc_stream(sst_drv_ctx, str_id);89}90 91/**92 * sst_realloc_stream - Send msg for (re-)allocating a stream using the93 * @sst_drv_ctx: intel_sst_drv context pointer94 * @str_id: stream ID95 *96 * Send a msg for (re-)allocating a stream using the parameters previously97 * passed to sst_alloc_stream_mrfld() for the same stream ID.98 * Return: 0 or negative errno value.99 */100int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)101{102 struct snd_sst_alloc_response *response;103 struct stream_info *str_info;104 void *data = NULL;105 int ret;106 107 str_info = get_stream_info(sst_drv_ctx, str_id);108 if (!str_info)109 return -EINVAL;110 111 dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",112 str_id, str_info->pipe_id);113 114 ret = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,115 IPC_IA_ALLOC_STREAM_MRFLD, str_info->pipe_id,116 sizeof(str_info->alloc_param), &str_info->alloc_param,117 &data, true, true, false, true);118 119 if (ret < 0) {120 dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);121 /* alloc failed, so reset the state to uninit */122 str_info->status = STREAM_UN_INIT;123 str_id = ret;124 } else if (data) {125 response = (struct snd_sst_alloc_response *)data;126 ret = response->str_type.result;127 if (!ret)128 goto out;129 dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);130 if (ret == SST_ERR_STREAM_IN_USE) {131 dev_err(sst_drv_ctx->dev,132 "FW not in clean state, send free for:%d\n", str_id);133 sst_free_stream(sst_drv_ctx, str_id);134 }135 str_id = -ret;136 }137out:138 kfree(data);139 return str_id;140}141 142/**143 * sst_start_stream - Send msg for a starting stream144 * @sst_drv_ctx: intel_sst_drv context pointer145 * @str_id: stream ID146 *147 * This function is called by any function which wants to start148 * a stream.149 */150int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)151{152 int retval = 0;153 struct stream_info *str_info;154 u16 data = 0;155 156 dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);157 str_info = get_stream_info(sst_drv_ctx, str_id);158 if (!str_info)159 return -EINVAL;160 if (str_info->status != STREAM_RUNNING)161 return -EBADRQC;162 163 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,164 IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,165 sizeof(u16), &data, NULL, true, true, true, false);166 167 return retval;168}169 170int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,171 struct snd_sst_bytes_v2 *bytes)172{ struct ipc_post *msg = NULL;173 u32 length;174 int pvt_id, ret = 0;175 struct sst_block *block = NULL;176 u8 bytes_block = bytes->block;177 178 dev_dbg(sst_drv_ctx->dev,179 "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",180 bytes->type, bytes->ipc_msg, bytes_block, bytes->task_id,181 bytes->pipe_id, bytes->len);182 183 if (sst_create_ipc_msg(&msg, true))184 return -ENOMEM;185 186 pvt_id = sst_assign_pvt_id(sst_drv_ctx);187 sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,188 bytes->task_id, 1, pvt_id);189 msg->mrfld_header.p.header_high.part.res_rqd = bytes_block;190 length = bytes->len;191 msg->mrfld_header.p.header_low_payload = length;192 dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);193 memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);194 if (bytes_block) {195 block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);196 if (block == NULL) {197 kfree(msg);198 ret = -ENOMEM;199 goto out;200 }201 }202 203 sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);204 dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",205 msg->mrfld_header.p.header_low_payload);206 207 if (bytes_block) {208 ret = sst_wait_timeout(sst_drv_ctx, block);209 if (ret) {210 dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);211 sst_free_block(sst_drv_ctx, block);212 goto out;213 }214 }215 if (bytes->type == SND_SST_BYTES_GET) {216 /*217 * copy the reply and send back218 * we need to update only sz and payload219 */220 if (bytes_block) {221 unsigned char *r = block->data;222 223 dev_dbg(sst_drv_ctx->dev, "read back %d bytes",224 bytes->len);225 memcpy(bytes->bytes, r, bytes->len);226 }227 }228 if (bytes_block)229 sst_free_block(sst_drv_ctx, block);230out:231 test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);232 return ret;233}234 235/**236 * sst_pause_stream - Send msg for a pausing stream237 * @sst_drv_ctx: intel_sst_drv context pointer238 * @str_id: stream ID239 *240 * This function is called by any function which wants to pause241 * an already running stream.242 */243int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)244{245 int retval = 0;246 struct stream_info *str_info;247 248 dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);249 str_info = get_stream_info(sst_drv_ctx, str_id);250 if (!str_info)251 return -EINVAL;252 if (str_info->status == STREAM_PAUSED)253 return 0;254 if (str_info->status == STREAM_RUNNING ||255 str_info->status == STREAM_INIT) {256 if (str_info->prev == STREAM_UN_INIT)257 return -EBADRQC;258 259 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,260 IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,261 0, NULL, NULL, true, true, false, true);262 263 if (retval == 0) {264 str_info->prev = str_info->status;265 str_info->status = STREAM_PAUSED;266 } else if (retval == -SST_ERR_INVALID_STREAM_ID) {267 retval = -EINVAL;268 mutex_lock(&sst_drv_ctx->sst_lock);269 sst_clean_stream(str_info);270 mutex_unlock(&sst_drv_ctx->sst_lock);271 }272 } else {273 retval = -EBADRQC;274 dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n");275 }276 277 return retval;278}279 280/**281 * sst_resume_stream - Send msg for resuming stream282 * @sst_drv_ctx: intel_sst_drv context pointer283 * @str_id: stream ID284 *285 * This function is called by any function which wants to resume286 * an already paused stream.287 */288int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)289{290 int retval = 0;291 struct stream_info *str_info;292 293 dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);294 str_info = get_stream_info(sst_drv_ctx, str_id);295 if (!str_info)296 return -EINVAL;297 if (str_info->status == STREAM_RUNNING)298 return 0;299 300 if (str_info->resume_status == STREAM_PAUSED &&301 str_info->resume_prev == STREAM_RUNNING) {302 /*303 * Stream was running before suspend and re-created on resume,304 * start it to get back to running state.305 */306 dev_dbg(sst_drv_ctx->dev, "restart recreated stream after resume\n");307 str_info->status = STREAM_RUNNING;308 str_info->prev = STREAM_PAUSED;309 retval = sst_start_stream(sst_drv_ctx, str_id);310 str_info->resume_status = STREAM_UN_INIT;311 } else if (str_info->resume_status == STREAM_PAUSED &&312 str_info->resume_prev == STREAM_INIT) {313 /*314 * Stream was idle before suspend and re-created on resume,315 * keep it as is.316 */317 dev_dbg(sst_drv_ctx->dev, "leaving recreated stream idle after resume\n");318 str_info->status = STREAM_INIT;319 str_info->prev = STREAM_PAUSED;320 str_info->resume_status = STREAM_UN_INIT;321 } else if (str_info->status == STREAM_PAUSED) {322 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,323 IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,324 str_info->pipe_id, 0, NULL, NULL,325 true, true, false, true);326 327 if (!retval) {328 if (str_info->prev == STREAM_RUNNING)329 str_info->status = STREAM_RUNNING;330 else331 str_info->status = STREAM_INIT;332 str_info->prev = STREAM_PAUSED;333 } else if (retval == -SST_ERR_INVALID_STREAM_ID) {334 retval = -EINVAL;335 mutex_lock(&sst_drv_ctx->sst_lock);336 sst_clean_stream(str_info);337 mutex_unlock(&sst_drv_ctx->sst_lock);338 }339 } else {340 retval = -EBADRQC;341 dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");342 }343 344 return retval;345}346 347 348/**349 * sst_drop_stream - Send msg for stopping stream350 * @sst_drv_ctx: intel_sst_drv context pointer351 * @str_id: stream ID352 *353 * This function is called by any function which wants to stop354 * a stream.355 */356int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)357{358 int retval = 0;359 struct stream_info *str_info;360 361 dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);362 str_info = get_stream_info(sst_drv_ctx, str_id);363 if (!str_info)364 return -EINVAL;365 366 if (str_info->status != STREAM_UN_INIT) {367 str_info->prev = STREAM_UN_INIT;368 str_info->status = STREAM_INIT;369 str_info->cumm_bytes = 0;370 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,371 IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,372 str_info->pipe_id, 0, NULL, NULL,373 true, true, true, false);374 } else {375 retval = -EBADRQC;376 dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",377 str_info->status);378 }379 return retval;380}381 382/**383 * sst_drain_stream - Send msg for draining stream384 * @sst_drv_ctx: intel_sst_drv context pointer385 * @str_id: stream ID386 * @partial_drain: boolean indicating if a gapless transition is taking place387 *388 * This function is called by any function which wants to drain389 * a stream.390 */391int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,392 int str_id, bool partial_drain)393{394 int retval = 0;395 struct stream_info *str_info;396 397 dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);398 str_info = get_stream_info(sst_drv_ctx, str_id);399 if (!str_info)400 return -EINVAL;401 if (str_info->status != STREAM_RUNNING &&402 str_info->status != STREAM_INIT &&403 str_info->status != STREAM_PAUSED) {404 dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",405 str_info->status);406 return -EBADRQC;407 }408 409 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,410 IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,411 sizeof(u8), &partial_drain, NULL, true, true, false, false);412 /*413 * with new non blocked drain implementation in core we dont need to414 * wait for respsonse, and need to only invoke callback for drain415 * complete416 */417 418 return retval;419}420 421/**422 * sst_free_stream - Frees a stream423 * @sst_drv_ctx: intel_sst_drv context pointer424 * @str_id: stream ID425 *426 * This function is called by any function which wants to free427 * a stream.428 */429int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)430{431 int retval = 0;432 struct stream_info *str_info;433 434 dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);435 436 mutex_lock(&sst_drv_ctx->sst_lock);437 if (sst_drv_ctx->sst_state == SST_RESET) {438 mutex_unlock(&sst_drv_ctx->sst_lock);439 return -ENODEV;440 }441 mutex_unlock(&sst_drv_ctx->sst_lock);442 str_info = get_stream_info(sst_drv_ctx, str_id);443 if (!str_info)444 return -EINVAL;445 446 mutex_lock(&str_info->lock);447 if (str_info->status != STREAM_UN_INIT) {448 str_info->prev = str_info->status;449 str_info->status = STREAM_UN_INIT;450 mutex_unlock(&str_info->lock);451 452 dev_dbg(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",453 str_id, str_info->pipe_id);454 retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,455 IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,456 NULL, NULL, true, true, false, true);457 458 dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",459 retval);460 mutex_lock(&sst_drv_ctx->sst_lock);461 sst_clean_stream(str_info);462 mutex_unlock(&sst_drv_ctx->sst_lock);463 dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");464 } else {465 mutex_unlock(&str_info->lock);466 retval = -EBADRQC;467 dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");468 }469 470 return retval;471}472