linux/sound/soc/intel/atom/sst/sst_stream.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  sst_stream.c - Intel SST Driver for audio engine
   4 *
   5 *  Copyright (C) 2008-14 Intel Corp
   6 *  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 <linux/pm_runtime.h>
  19#include <sound/core.h>
  20#include <sound/pcm.h>
  21#include <sound/soc.h>
  22#include <sound/compress_driver.h>
  23#include <asm/platform_sst_audio.h>
  24#include "../sst-mfld-platform.h"
  25#include "sst.h"
  26
  27int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
  28{
  29        struct snd_pcm_params *pcm_params;
  30        struct snd_sst_params *str_params;
  31        struct snd_sst_tstamp fw_tstamp;
  32        struct stream_info *str_info;
  33        int i, num_ch, str_id;
  34
  35        dev_dbg(sst_drv_ctx->dev, "Enter\n");
  36
  37        str_params = (struct snd_sst_params *)params;
  38        str_id = str_params->stream_id;
  39        str_info = get_stream_info(sst_drv_ctx, str_id);
  40        if (!str_info)
  41                return -EINVAL;
  42
  43        memset(&str_info->alloc_param, 0, sizeof(str_info->alloc_param));
  44        str_info->alloc_param.operation = str_params->ops;
  45        str_info->alloc_param.codec_type = str_params->codec;
  46        str_info->alloc_param.sg_count = str_params->aparams.sg_count;
  47        str_info->alloc_param.ring_buf_info[0].addr =
  48                str_params->aparams.ring_buf_info[0].addr;
  49        str_info->alloc_param.ring_buf_info[0].size =
  50                str_params->aparams.ring_buf_info[0].size;
  51        str_info->alloc_param.frag_size = str_params->aparams.frag_size;
  52
  53        memcpy(&str_info->alloc_param.codec_params, &str_params->sparams,
  54                        sizeof(struct snd_sst_stream_params));
  55
  56        /*
  57         * fill channel map params for multichannel support.
  58         * Ideally channel map should be received from upper layers
  59         * for multichannel support.
  60         * Currently hardcoding as per FW reqm.
  61         */
  62        num_ch = sst_get_num_channel(str_params);
  63        pcm_params = &str_info->alloc_param.codec_params.uc.pcm_params;
  64        for (i = 0; i < 8; i++) {
  65                if (i < num_ch)
  66                        pcm_params->channel_map[i] = i;
  67                else
  68                        pcm_params->channel_map[i] = 0xff;
  69        }
  70
  71        sst_drv_ctx->streams[str_id].status = STREAM_INIT;
  72        sst_drv_ctx->streams[str_id].prev = STREAM_UN_INIT;
  73        sst_drv_ctx->streams[str_id].pipe_id = str_params->device_type;
  74        sst_drv_ctx->streams[str_id].task_id = str_params->task;
  75        sst_drv_ctx->streams[str_id].num_ch = num_ch;
  76
  77        if (sst_drv_ctx->info.lpe_viewpt_rqd)
  78                str_info->alloc_param.ts = sst_drv_ctx->info.mailbox_start +
  79                        sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
  80        else
  81                str_info->alloc_param.ts = sst_drv_ctx->mailbox_add +
  82                        sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
  83
  84        dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
  85                        str_info->alloc_param.ts);
  86        dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
  87                        str_info->pipe_id, str_info->task_id);
  88
  89        return sst_realloc_stream(sst_drv_ctx, str_id);
  90}
  91
  92/**
  93 * sst_realloc_stream - Send msg for (re-)allocating a stream using the
  94 * @sst_drv_ctx: intel_sst_drv context pointer
  95 * @str_id: stream ID
  96 *
  97 * Send a msg for (re-)allocating a stream using the parameters previously
  98 * passed to sst_alloc_stream_mrfld() for the same stream ID.
  99 * Return: 0 or negative errno value.
 100 */
 101int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
 102{
 103        struct snd_sst_alloc_response *response;
 104        struct stream_info *str_info;
 105        void *data = NULL;
 106        int ret;
 107
 108        str_info = get_stream_info(sst_drv_ctx, str_id);
 109        if (!str_info)
 110                return -EINVAL;
 111
 112        dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
 113                str_id, str_info->pipe_id);
 114
 115        ret = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
 116                        IPC_IA_ALLOC_STREAM_MRFLD, str_info->pipe_id,
 117                        sizeof(str_info->alloc_param), &str_info->alloc_param,
 118                        &data, true, true, false, true);
 119
 120        if (ret < 0) {
 121                dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
 122                /* alloc failed, so reset the state to uninit */
 123                str_info->status = STREAM_UN_INIT;
 124                str_id = ret;
 125        } else if (data) {
 126                response = (struct snd_sst_alloc_response *)data;
 127                ret = response->str_type.result;
 128                if (!ret)
 129                        goto out;
 130                dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
 131                if (ret == SST_ERR_STREAM_IN_USE) {
 132                        dev_err(sst_drv_ctx->dev,
 133                                "FW not in clean state, send free for:%d\n", str_id);
 134                        sst_free_stream(sst_drv_ctx, str_id);
 135                }
 136                str_id = -ret;
 137        }
 138out:
 139        kfree(data);
 140        return str_id;
 141}
 142
 143/**
 144 * sst_start_stream - Send msg for a starting stream
 145 * @sst_drv_ctx: intel_sst_drv context pointer
 146 * @str_id: stream ID
 147 *
 148 * This function is called by any function which wants to start
 149 * a stream.
 150 */
 151int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
 152{
 153        int retval = 0;
 154        struct stream_info *str_info;
 155        u16 data = 0;
 156
 157        dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
 158        str_info = get_stream_info(sst_drv_ctx, str_id);
 159        if (!str_info)
 160                return -EINVAL;
 161        if (str_info->status != STREAM_RUNNING)
 162                return -EBADRQC;
 163
 164        retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
 165                        IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
 166                        sizeof(u16), &data, NULL, true, true, true, false);
 167
 168        return retval;
 169}
 170
 171int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
 172                struct snd_sst_bytes_v2 *bytes)
 173{       struct ipc_post *msg = NULL;
 174        u32 length;
 175        int pvt_id, ret = 0;
 176        struct sst_block *block = NULL;
 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 back
 218                 * we need to update only sz and payload
 219                 */
 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 stream
 237 * @sst_drv_ctx: intel_sst_drv context pointer
 238 * @str_id: stream ID
 239 *
 240 * This function is called by any function which wants to pause
 241 * 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 stream
 282 * @sst_drv_ctx: intel_sst_drv context pointer
 283 * @str_id: stream ID
 284 *
 285 * This function is called by any function which wants to resume
 286 * 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                        else
 331                                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 stream
 350 * @sst_drv_ctx: intel_sst_drv context pointer
 351 * @str_id: stream ID
 352 *
 353 * This function is called by any function which wants to stop
 354 * 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 stream
 384 * @sst_drv_ctx: intel_sst_drv context pointer
 385 * @str_id: stream ID
 386 * @partial_drain: boolean indicating if a gapless transition is taking place
 387 *
 388 * This function is called by any function which wants to drain
 389 * 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 to
 414         * wait for respsonse, and need to only invoke callback for drain
 415         * complete
 416         */
 417
 418        return retval;
 419}
 420
 421/**
 422 * sst_free_stream - Frees a stream
 423 * @sst_drv_ctx: intel_sst_drv context pointer
 424 * @str_id: stream ID
 425 *
 426 * This function is called by any function which wants to free
 427 * 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