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