linux/sound/soc/intel/skylake/cnl-sst.c
<<
>>
Prefs
   1/*
   2 * cnl-sst.c - DSP library functions for CNL platform
   3 *
   4 * Copyright (C) 2016-17, Intel Corporation.
   5 *
   6 * Author: Guneshwor Singh <guneshwor.o.singh@intel.com>
   7 *
   8 * Modified from:
   9 *      HDA DSP library functions for SKL platform
  10 *      Copyright (C) 2014-15, Intel Corporation.
  11 *
  12 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as version 2, as
  16 * published by the Free Software Foundation.
  17 *
  18 * This program is distributed in the hope that it will be useful, but
  19 * WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21 * General Public License for more details.
  22 *
  23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24 */
  25
  26#include <linux/module.h>
  27#include <linux/delay.h>
  28#include <linux/firmware.h>
  29#include <linux/device.h>
  30
  31#include "../common/sst-dsp.h"
  32#include "../common/sst-dsp-priv.h"
  33#include "../common/sst-ipc.h"
  34#include "cnl-sst-dsp.h"
  35#include "skl-sst-dsp.h"
  36#include "skl-sst-ipc.h"
  37
  38#define CNL_FW_ROM_INIT         0x1
  39#define CNL_FW_INIT             0x5
  40#define CNL_IPC_PURGE           0x01004000
  41#define CNL_INIT_TIMEOUT        300
  42#define CNL_BASEFW_TIMEOUT      3000
  43
  44#define CNL_ADSP_SRAM0_BASE     0x80000
  45
  46/* Firmware status window */
  47#define CNL_ADSP_FW_STATUS      CNL_ADSP_SRAM0_BASE
  48#define CNL_ADSP_ERROR_CODE     (CNL_ADSP_FW_STATUS + 0x4)
  49
  50#define CNL_INSTANCE_ID         0
  51#define CNL_BASE_FW_MODULE_ID   0
  52#define CNL_ADSP_FW_HDR_OFFSET  0x2000
  53#define CNL_ROM_CTRL_DMA_ID     0x9
  54
  55static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize)
  56{
  57
  58        int ret, stream_tag;
  59
  60        stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
  61        if (stream_tag <= 0) {
  62                dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag);
  63                return stream_tag;
  64        }
  65
  66        ctx->dsp_ops.stream_tag = stream_tag;
  67        memcpy(ctx->dmab.area, fwdata, fwsize);
  68
  69        /* purge FW request */
  70        sst_dsp_shim_write(ctx, CNL_ADSP_REG_HIPCIDR,
  71                           CNL_ADSP_REG_HIPCIDR_BUSY | (CNL_IPC_PURGE |
  72                           ((stream_tag - 1) << CNL_ROM_CTRL_DMA_ID)));
  73
  74        ret = cnl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK);
  75        if (ret < 0) {
  76                dev_err(ctx->dev, "dsp boot core failed ret: %d\n", ret);
  77                ret = -EIO;
  78                goto base_fw_load_failed;
  79        }
  80
  81        /* enable interrupt */
  82        cnl_ipc_int_enable(ctx);
  83        cnl_ipc_op_int_enable(ctx);
  84
  85        ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK,
  86                                    CNL_FW_ROM_INIT, CNL_INIT_TIMEOUT,
  87                                    "rom load");
  88        if (ret < 0) {
  89                dev_err(ctx->dev, "rom init timeout, ret: %d\n", ret);
  90                goto base_fw_load_failed;
  91        }
  92
  93        return 0;
  94
  95base_fw_load_failed:
  96        ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
  97        cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
  98
  99        return ret;
 100}
 101
 102static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
 103{
 104        int ret;
 105
 106        ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag);
 107        ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK,
 108                                    CNL_FW_INIT, CNL_BASEFW_TIMEOUT,
 109                                    "firmware boot");
 110
 111        ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag);
 112        ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag);
 113
 114        return ret;
 115}
 116
 117static int cnl_load_base_firmware(struct sst_dsp *ctx)
 118{
 119        struct firmware stripped_fw;
 120        struct skl_sst *cnl = ctx->thread_context;
 121        int ret;
 122
 123        if (!ctx->fw) {
 124                ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
 125                if (ret < 0) {
 126                        dev_err(ctx->dev, "request firmware failed: %d\n", ret);
 127                        goto cnl_load_base_firmware_failed;
 128                }
 129        }
 130
 131        /* parse uuids if first boot */
 132        if (cnl->is_first_boot) {
 133                ret = snd_skl_parse_uuids(ctx, ctx->fw,
 134                                          CNL_ADSP_FW_HDR_OFFSET, 0);
 135                if (ret < 0)
 136                        goto cnl_load_base_firmware_failed;
 137        }
 138
 139        stripped_fw.data = ctx->fw->data;
 140        stripped_fw.size = ctx->fw->size;
 141        skl_dsp_strip_extended_manifest(&stripped_fw);
 142
 143        ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
 144        if (ret < 0) {
 145                dev_err(ctx->dev, "prepare firmware failed: %d\n", ret);
 146                goto cnl_load_base_firmware_failed;
 147        }
 148
 149        ret = sst_transfer_fw_host_dma(ctx);
 150        if (ret < 0) {
 151                dev_err(ctx->dev, "transfer firmware failed: %d\n", ret);
 152                cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
 153                goto cnl_load_base_firmware_failed;
 154        }
 155
 156        ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete,
 157                                 msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
 158        if (ret == 0) {
 159                dev_err(ctx->dev, "FW ready timed-out\n");
 160                cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
 161                ret = -EIO;
 162                goto cnl_load_base_firmware_failed;
 163        }
 164
 165        cnl->fw_loaded = true;
 166
 167        return 0;
 168
 169cnl_load_base_firmware_failed:
 170        release_firmware(ctx->fw);
 171        ctx->fw = NULL;
 172
 173        return ret;
 174}
 175
 176static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 177{
 178        struct skl_sst *cnl = ctx->thread_context;
 179        unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 180        struct skl_ipc_dxstate_info dx;
 181        int ret;
 182
 183        if (!cnl->fw_loaded) {
 184                cnl->boot_complete = false;
 185                ret = cnl_load_base_firmware(ctx);
 186                if (ret < 0) {
 187                        dev_err(ctx->dev, "fw reload failed: %d\n", ret);
 188                        return ret;
 189                }
 190
 191                cnl->cores.state[core_id] = SKL_DSP_RUNNING;
 192                return ret;
 193        }
 194
 195        ret = cnl_dsp_enable_core(ctx, core_mask);
 196        if (ret < 0) {
 197                dev_err(ctx->dev, "enable dsp core %d failed: %d\n",
 198                        core_id, ret);
 199                goto err;
 200        }
 201
 202        if (core_id == SKL_DSP_CORE0_ID) {
 203                /* enable interrupt */
 204                cnl_ipc_int_enable(ctx);
 205                cnl_ipc_op_int_enable(ctx);
 206                cnl->boot_complete = false;
 207
 208                ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete,
 209                                         msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
 210                if (ret == 0) {
 211                        dev_err(ctx->dev,
 212                                "dsp boot timeout, status=%#x error=%#x\n",
 213                                sst_dsp_shim_read(ctx, CNL_ADSP_FW_STATUS),
 214                                sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE));
 215                        goto err;
 216                }
 217        } else {
 218                dx.core_mask = core_mask;
 219                dx.dx_mask = core_mask;
 220
 221                ret = skl_ipc_set_dx(&cnl->ipc, CNL_INSTANCE_ID,
 222                                     CNL_BASE_FW_MODULE_ID, &dx);
 223                if (ret < 0) {
 224                        dev_err(ctx->dev, "set_dx failed, core: %d ret: %d\n",
 225                                core_id, ret);
 226                        goto err;
 227                }
 228        }
 229        cnl->cores.state[core_id] = SKL_DSP_RUNNING;
 230
 231        return 0;
 232err:
 233        cnl_dsp_disable_core(ctx, core_mask);
 234
 235        return ret;
 236}
 237
 238static int cnl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
 239{
 240        struct skl_sst *cnl = ctx->thread_context;
 241        unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 242        struct skl_ipc_dxstate_info dx;
 243        int ret;
 244
 245        dx.core_mask = core_mask;
 246        dx.dx_mask = SKL_IPC_D3_MASK;
 247
 248        ret = skl_ipc_set_dx(&cnl->ipc, CNL_INSTANCE_ID,
 249                             CNL_BASE_FW_MODULE_ID, &dx);
 250        if (ret < 0) {
 251                dev_err(ctx->dev,
 252                        "dsp core %d to d3 failed; continue reset\n",
 253                        core_id);
 254                cnl->fw_loaded = false;
 255        }
 256
 257        /* disable interrupts if core 0 */
 258        if (core_id == SKL_DSP_CORE0_ID) {
 259                skl_ipc_op_int_disable(ctx);
 260                skl_ipc_int_disable(ctx);
 261        }
 262
 263        ret = cnl_dsp_disable_core(ctx, core_mask);
 264        if (ret < 0) {
 265                dev_err(ctx->dev, "disable dsp core %d failed: %d\n",
 266                        core_id, ret);
 267                return ret;
 268        }
 269
 270        cnl->cores.state[core_id] = SKL_DSP_RESET;
 271
 272        return ret;
 273}
 274
 275static unsigned int cnl_get_errno(struct sst_dsp *ctx)
 276{
 277        return sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE);
 278}
 279
 280static const struct skl_dsp_fw_ops cnl_fw_ops = {
 281        .set_state_D0 = cnl_set_dsp_D0,
 282        .set_state_D3 = cnl_set_dsp_D3,
 283        .load_fw = cnl_load_base_firmware,
 284        .get_fw_errcode = cnl_get_errno,
 285};
 286
 287static struct sst_ops cnl_ops = {
 288        .irq_handler = cnl_dsp_sst_interrupt,
 289        .write = sst_shim32_write,
 290        .read = sst_shim32_read,
 291        .ram_read = sst_memcpy_fromio_32,
 292        .ram_write = sst_memcpy_toio_32,
 293        .free = cnl_dsp_free,
 294};
 295
 296#define CNL_IPC_GLB_NOTIFY_RSP_SHIFT    29
 297#define CNL_IPC_GLB_NOTIFY_RSP_MASK     0x1
 298#define CNL_IPC_GLB_NOTIFY_RSP_TYPE(x)  (((x) >> CNL_IPC_GLB_NOTIFY_RSP_SHIFT) \
 299                                        & CNL_IPC_GLB_NOTIFY_RSP_MASK)
 300
 301static irqreturn_t cnl_dsp_irq_thread_handler(int irq, void *context)
 302{
 303        struct sst_dsp *dsp = context;
 304        struct skl_sst *cnl = sst_dsp_get_thread_context(dsp);
 305        struct sst_generic_ipc *ipc = &cnl->ipc;
 306        struct skl_ipc_header header = {0};
 307        u32 hipcida, hipctdr, hipctdd;
 308        int ipc_irq = 0;
 309
 310        /* here we handle ipc interrupts only */
 311        if (!(dsp->intr_status & CNL_ADSPIS_IPC))
 312                return IRQ_NONE;
 313
 314        hipcida = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCIDA);
 315        hipctdr = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCTDR);
 316
 317        /* reply message from dsp */
 318        if (hipcida & CNL_ADSP_REG_HIPCIDA_DONE) {
 319                sst_dsp_shim_update_bits(dsp, CNL_ADSP_REG_HIPCCTL,
 320                        CNL_ADSP_REG_HIPCCTL_DONE, 0);
 321
 322                /* clear done bit - tell dsp operation is complete */
 323                sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCIDA,
 324                        CNL_ADSP_REG_HIPCIDA_DONE, CNL_ADSP_REG_HIPCIDA_DONE);
 325
 326                ipc_irq = 1;
 327
 328                /* unmask done interrupt */
 329                sst_dsp_shim_update_bits(dsp, CNL_ADSP_REG_HIPCCTL,
 330                        CNL_ADSP_REG_HIPCCTL_DONE, CNL_ADSP_REG_HIPCCTL_DONE);
 331        }
 332
 333        /* new message from dsp */
 334        if (hipctdr & CNL_ADSP_REG_HIPCTDR_BUSY) {
 335                hipctdd = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCTDD);
 336                header.primary = hipctdr;
 337                header.extension = hipctdd;
 338                dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x",
 339                                                header.primary);
 340                dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
 341                                                header.extension);
 342
 343                if (CNL_IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
 344                        /* Handle Immediate reply from DSP Core */
 345                        skl_ipc_process_reply(ipc, header);
 346                } else {
 347                        dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n");
 348                        skl_ipc_process_notification(ipc, header);
 349                }
 350                /* clear busy interrupt */
 351                sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCTDR,
 352                        CNL_ADSP_REG_HIPCTDR_BUSY, CNL_ADSP_REG_HIPCTDR_BUSY);
 353
 354                /* set done bit to ack dsp */
 355                sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCTDA,
 356                        CNL_ADSP_REG_HIPCTDA_DONE, CNL_ADSP_REG_HIPCTDA_DONE);
 357                ipc_irq = 1;
 358        }
 359
 360        if (ipc_irq == 0)
 361                return IRQ_NONE;
 362
 363        cnl_ipc_int_enable(dsp);
 364
 365        /* continue to send any remaining messages */
 366        schedule_work(&ipc->kwork);
 367
 368        return IRQ_HANDLED;
 369}
 370
 371static struct sst_dsp_device cnl_dev = {
 372        .thread = cnl_dsp_irq_thread_handler,
 373        .ops = &cnl_ops,
 374};
 375
 376static void cnl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
 377{
 378        struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->header);
 379
 380        if (msg->tx_size)
 381                sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
 382        sst_dsp_shim_write_unlocked(ipc->dsp, CNL_ADSP_REG_HIPCIDD,
 383                                    header->extension);
 384        sst_dsp_shim_write_unlocked(ipc->dsp, CNL_ADSP_REG_HIPCIDR,
 385                                header->primary | CNL_ADSP_REG_HIPCIDR_BUSY);
 386}
 387
 388static bool cnl_ipc_is_dsp_busy(struct sst_dsp *dsp)
 389{
 390        u32 hipcidr;
 391
 392        hipcidr = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCIDR);
 393
 394        return (hipcidr & CNL_ADSP_REG_HIPCIDR_BUSY);
 395}
 396
 397static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl)
 398{
 399        struct sst_generic_ipc *ipc;
 400        int err;
 401
 402        ipc = &cnl->ipc;
 403        ipc->dsp = cnl->dsp;
 404        ipc->dev = dev;
 405
 406        ipc->tx_data_max_size = CNL_ADSP_W1_SZ;
 407        ipc->rx_data_max_size = CNL_ADSP_W0_UP_SZ;
 408
 409        err = sst_ipc_init(ipc);
 410        if (err)
 411                return err;
 412
 413        /*
 414         * overriding tx_msg and is_dsp_busy since
 415         * ipc registers are different for cnl
 416         */
 417        ipc->ops.tx_msg = cnl_ipc_tx_msg;
 418        ipc->ops.tx_data_copy = skl_ipc_tx_data_copy;
 419        ipc->ops.is_dsp_busy = cnl_ipc_is_dsp_busy;
 420
 421        return 0;
 422}
 423
 424int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 425                     const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
 426                     struct skl_sst **dsp)
 427{
 428        struct skl_sst *cnl;
 429        struct sst_dsp *sst;
 430        int ret;
 431
 432        ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev);
 433        if (ret < 0) {
 434                dev_err(dev, "%s: no device\n", __func__);
 435                return ret;
 436        }
 437
 438        cnl = *dsp;
 439        sst = cnl->dsp;
 440        sst->fw_ops = cnl_fw_ops;
 441        sst->addr.lpe = mmio_base;
 442        sst->addr.shim = mmio_base;
 443        sst->addr.sram0_base = CNL_ADSP_SRAM0_BASE;
 444        sst->addr.sram1_base = CNL_ADSP_SRAM1_BASE;
 445        sst->addr.w0_stat_sz = CNL_ADSP_W0_STAT_SZ;
 446        sst->addr.w0_up_sz = CNL_ADSP_W0_UP_SZ;
 447
 448        sst_dsp_mailbox_init(sst, (CNL_ADSP_SRAM0_BASE + CNL_ADSP_W0_STAT_SZ),
 449                             CNL_ADSP_W0_UP_SZ, CNL_ADSP_SRAM1_BASE,
 450                             CNL_ADSP_W1_SZ);
 451
 452        ret = cnl_ipc_init(dev, cnl);
 453        if (ret) {
 454                skl_dsp_free(sst);
 455                return ret;
 456        }
 457
 458        cnl->boot_complete = false;
 459        init_waitqueue_head(&cnl->boot_wait);
 460
 461        return skl_dsp_acquire_irq(sst);
 462}
 463EXPORT_SYMBOL_GPL(cnl_sst_dsp_init);
 464
 465int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
 466{
 467        int ret;
 468        struct sst_dsp *sst = ctx->dsp;
 469
 470        ret = ctx->dsp->fw_ops.load_fw(sst);
 471        if (ret < 0) {
 472                dev_err(dev, "load base fw failed: %d", ret);
 473                return ret;
 474        }
 475
 476        skl_dsp_init_core_state(sst);
 477
 478        ctx->is_first_boot = false;
 479
 480        return 0;
 481}
 482EXPORT_SYMBOL_GPL(cnl_sst_init_fw);
 483
 484void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 485{
 486        if (ctx->dsp->fw)
 487                release_firmware(ctx->dsp->fw);
 488
 489        skl_freeup_uuid_list(ctx);
 490        cnl_ipc_free(&ctx->ipc);
 491
 492        ctx->dsp->ops->free(ctx->dsp);
 493}
 494EXPORT_SYMBOL_GPL(cnl_sst_dsp_cleanup);
 495
 496MODULE_LICENSE("GPL v2");
 497MODULE_DESCRIPTION("Intel Cannonlake IPC driver");
 498