brintos

brintos / linux-shallow public Read only

0
0
Text · 13.7 KiB · 288221d Raw
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