162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sst.c - Intel SST Driver for audio engine 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008-14 Intel Corp 662306a36Sopenharmony_ci * Authors: Vinod Koul <vinod.koul@intel.com> 762306a36Sopenharmony_ci * Harsha Priya <priya.harsha@intel.com> 862306a36Sopenharmony_ci * Dharageswari R <dharageswari.r@intel.com> 962306a36Sopenharmony_ci * KP Jeeja <jeeja.kp@intel.com> 1062306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/fs.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/firmware.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci#include <linux/pm_qos.h> 2262306a36Sopenharmony_ci#include <linux/async.h> 2362306a36Sopenharmony_ci#include <linux/acpi.h> 2462306a36Sopenharmony_ci#include <linux/sysfs.h> 2562306a36Sopenharmony_ci#include <sound/core.h> 2662306a36Sopenharmony_ci#include <sound/soc.h> 2762306a36Sopenharmony_ci#include <asm/platform_sst_audio.h> 2862306a36Sopenharmony_ci#include "../sst-mfld-platform.h" 2962306a36Sopenharmony_ci#include "sst.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciMODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); 3262306a36Sopenharmony_ciMODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); 3362306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline bool sst_is_process_reply(u32 msg_id) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci return ((msg_id & PROCESS_MSG) ? true : false); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline bool sst_validate_mailbox_size(unsigned int size) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return ((size <= SST_MAILBOX_SIZE) ? true : false); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci union interrupt_reg_mrfld isr; 4962306a36Sopenharmony_ci union ipc_header_mrfld header; 5062306a36Sopenharmony_ci union sst_imr_reg_mrfld imr; 5162306a36Sopenharmony_ci struct ipc_post *msg = NULL; 5262306a36Sopenharmony_ci unsigned int size; 5362306a36Sopenharmony_ci struct intel_sst_drv *drv = (struct intel_sst_drv *) context; 5462306a36Sopenharmony_ci irqreturn_t retval = IRQ_HANDLED; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Interrupt arrived, check src */ 5762306a36Sopenharmony_ci isr.full = sst_shim_read64(drv->shim, SST_ISRX); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (isr.part.done_interrupt) { 6062306a36Sopenharmony_ci /* Clear done bit */ 6162306a36Sopenharmony_ci spin_lock(&drv->ipc_spin_lock); 6262306a36Sopenharmony_ci header.full = sst_shim_read64(drv->shim, 6362306a36Sopenharmony_ci drv->ipc_reg.ipcx); 6462306a36Sopenharmony_ci header.p.header_high.part.done = 0; 6562306a36Sopenharmony_ci sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* write 1 to clear status register */; 6862306a36Sopenharmony_ci isr.part.done_interrupt = 1; 6962306a36Sopenharmony_ci sst_shim_write64(drv->shim, SST_ISRX, isr.full); 7062306a36Sopenharmony_ci spin_unlock(&drv->ipc_spin_lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* we can send more messages to DSP so trigger work */ 7362306a36Sopenharmony_ci queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq); 7462306a36Sopenharmony_ci retval = IRQ_HANDLED; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (isr.part.busy_interrupt) { 7862306a36Sopenharmony_ci /* message from dsp so copy that */ 7962306a36Sopenharmony_ci spin_lock(&drv->ipc_spin_lock); 8062306a36Sopenharmony_ci imr.full = sst_shim_read64(drv->shim, SST_IMRX); 8162306a36Sopenharmony_ci imr.part.busy_interrupt = 1; 8262306a36Sopenharmony_ci sst_shim_write64(drv->shim, SST_IMRX, imr.full); 8362306a36Sopenharmony_ci spin_unlock(&drv->ipc_spin_lock); 8462306a36Sopenharmony_ci header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) { 8762306a36Sopenharmony_ci drv->ops->clear_interrupt(drv); 8862306a36Sopenharmony_ci return IRQ_HANDLED; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (header.p.header_high.part.large) { 9262306a36Sopenharmony_ci size = header.p.header_low_payload; 9362306a36Sopenharmony_ci if (sst_validate_mailbox_size(size)) { 9462306a36Sopenharmony_ci memcpy_fromio(msg->mailbox_data, 9562306a36Sopenharmony_ci drv->mailbox + drv->mailbox_recv_offset, size); 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci dev_err(drv->dev, 9862306a36Sopenharmony_ci "Mailbox not copied, payload size is: %u\n", size); 9962306a36Sopenharmony_ci header.p.header_low_payload = 0; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci msg->mrfld_header = header; 10462306a36Sopenharmony_ci msg->is_process_reply = 10562306a36Sopenharmony_ci sst_is_process_reply(header.p.header_high.part.msg_id); 10662306a36Sopenharmony_ci spin_lock(&drv->rx_msg_lock); 10762306a36Sopenharmony_ci list_add_tail(&msg->node, &drv->rx_list); 10862306a36Sopenharmony_ci spin_unlock(&drv->rx_msg_lock); 10962306a36Sopenharmony_ci drv->ops->clear_interrupt(drv); 11062306a36Sopenharmony_ci retval = IRQ_WAKE_THREAD; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci return retval; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct intel_sst_drv *drv = (struct intel_sst_drv *) context; 11862306a36Sopenharmony_ci struct ipc_post *__msg, *msg; 11962306a36Sopenharmony_ci unsigned long irq_flags; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); 12262306a36Sopenharmony_ci if (list_empty(&drv->rx_list)) { 12362306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); 12462306a36Sopenharmony_ci return IRQ_HANDLED; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) { 12862306a36Sopenharmony_ci list_del(&msg->node); 12962306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); 13062306a36Sopenharmony_ci if (msg->is_process_reply) 13162306a36Sopenharmony_ci drv->ops->process_message(msg); 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci drv->ops->process_reply(drv, msg); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (msg->is_large) 13662306a36Sopenharmony_ci kfree(msg->mailbox_data); 13762306a36Sopenharmony_ci kfree(msg); 13862306a36Sopenharmony_ci spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); 14162306a36Sopenharmony_ci return IRQ_HANDLED; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int sst_save_dsp_context_v2(struct intel_sst_drv *sst) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int ret = 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD, 14962306a36Sopenharmony_ci IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL, 15062306a36Sopenharmony_ci true, true, false, true); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (ret < 0) { 15362306a36Sopenharmony_ci dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret); 15462306a36Sopenharmony_ci return -EIO; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct intel_sst_ops mrfld_ops = { 16262306a36Sopenharmony_ci .interrupt = intel_sst_interrupt_mrfld, 16362306a36Sopenharmony_ci .irq_thread = intel_sst_irq_thread_mrfld, 16462306a36Sopenharmony_ci .clear_interrupt = intel_sst_clear_intr_mrfld, 16562306a36Sopenharmony_ci .start = sst_start_mrfld, 16662306a36Sopenharmony_ci .reset = intel_sst_reset_dsp_mrfld, 16762306a36Sopenharmony_ci .post_message = sst_post_message_mrfld, 16862306a36Sopenharmony_ci .process_reply = sst_process_reply_mrfld, 16962306a36Sopenharmony_ci .save_dsp_context = sst_save_dsp_context_v2, 17062306a36Sopenharmony_ci .alloc_stream = sst_alloc_stream_mrfld, 17162306a36Sopenharmony_ci .post_download = sst_post_download_mrfld, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciint sst_driver_ops(struct intel_sst_drv *sst) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci switch (sst->dev_id) { 17862306a36Sopenharmony_ci case PCI_DEVICE_ID_INTEL_SST_TNG: 17962306a36Sopenharmony_ci case PCI_DEVICE_ID_INTEL_SST_BYT: 18062306a36Sopenharmony_ci case PCI_DEVICE_ID_INTEL_SST_BSW: 18162306a36Sopenharmony_ci sst->tstamp = SST_TIME_STAMP_MRFLD; 18262306a36Sopenharmony_ci sst->ops = &mrfld_ops; 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci dev_err(sst->dev, 18762306a36Sopenharmony_ci "SST Driver capabilities missing for dev_id: %x", 18862306a36Sopenharmony_ci sst->dev_id); 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid sst_process_pending_msg(struct work_struct *work) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct intel_sst_drv *ctx = container_of(work, 19662306a36Sopenharmony_ci struct intel_sst_drv, ipc_post_msg_wq); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ctx->ops->post_message(ctx, NULL, false); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int sst_workqueue_init(struct intel_sst_drv *ctx) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->memcpy_list); 20462306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->rx_list); 20562306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->ipc_dispatch_list); 20662306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->block_list); 20762306a36Sopenharmony_ci INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg); 20862306a36Sopenharmony_ci init_waitqueue_head(&ctx->wait_queue); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ctx->post_msg_wq = 21162306a36Sopenharmony_ci create_singlethread_workqueue("sst_post_msg_wq"); 21262306a36Sopenharmony_ci if (!ctx->post_msg_wq) 21362306a36Sopenharmony_ci return -EBUSY; 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void sst_init_locks(struct intel_sst_drv *ctx) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci mutex_init(&ctx->sst_lock); 22062306a36Sopenharmony_ci spin_lock_init(&ctx->rx_msg_lock); 22162306a36Sopenharmony_ci spin_lock_init(&ctx->ipc_spin_lock); 22262306a36Sopenharmony_ci spin_lock_init(&ctx->block_lock); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * Driver handles PCI IDs in ACPI - sst_acpi_probe() - and we are using only 22762306a36Sopenharmony_ci * device ID part. If real ACPI ID appears, the kstrtouint() returns error, so 22862306a36Sopenharmony_ci * we are fine with using unsigned short as dev_id type. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ciint sst_alloc_drv_context(struct intel_sst_drv **ctx, 23162306a36Sopenharmony_ci struct device *dev, unsigned short dev_id) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL); 23462306a36Sopenharmony_ci if (!(*ctx)) 23562306a36Sopenharmony_ci return -ENOMEM; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci (*ctx)->dev = dev; 23862306a36Sopenharmony_ci (*ctx)->dev_id = dev_id; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_alloc_drv_context); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic ssize_t firmware_version_show(struct device *dev, 24562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct intel_sst_drv *ctx = dev_get_drvdata(dev); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 && 25062306a36Sopenharmony_ci ctx->fw_version.minor == 0 && ctx->fw_version.build == 0) 25162306a36Sopenharmony_ci return sysfs_emit(buf, "FW not yet loaded\n"); 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci return sysfs_emit(buf, "v%02x.%02x.%02x.%02x\n", 25462306a36Sopenharmony_ci ctx->fw_version.type, ctx->fw_version.major, 25562306a36Sopenharmony_ci ctx->fw_version.minor, ctx->fw_version.build); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_version); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic const struct attribute *sst_fw_version_attrs[] = { 26262306a36Sopenharmony_ci &dev_attr_firmware_version.attr, 26362306a36Sopenharmony_ci NULL, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic const struct attribute_group sst_fw_version_attr_group = { 26762306a36Sopenharmony_ci .attrs = (struct attribute **)sst_fw_version_attrs, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciint sst_context_init(struct intel_sst_drv *ctx) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int ret = 0, i; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!ctx->pdata) 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!ctx->pdata->probe_data) 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = sst_driver_ops(ctx); 28362306a36Sopenharmony_ci if (ret != 0) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci sst_init_locks(ctx); 28762306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_RESET); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* pvt_id 0 reserved for async messages */ 29062306a36Sopenharmony_ci ctx->pvt_id = 1; 29162306a36Sopenharmony_ci ctx->stream_cnt = 0; 29262306a36Sopenharmony_ci ctx->fw_in_mem = NULL; 29362306a36Sopenharmony_ci /* we use memcpy, so set to 0 */ 29462306a36Sopenharmony_ci ctx->use_dma = 0; 29562306a36Sopenharmony_ci ctx->use_lli = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (sst_workqueue_init(ctx)) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off; 30162306a36Sopenharmony_ci ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset; 30262306a36Sopenharmony_ci ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev_info(ctx->dev, "Got drv data max stream %d\n", 30562306a36Sopenharmony_ci ctx->info.max_streams); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for (i = 1; i <= ctx->info.max_streams; i++) { 30862306a36Sopenharmony_ci struct stream_info *stream = &ctx->streams[i]; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci memset(stream, 0, sizeof(*stream)); 31162306a36Sopenharmony_ci stream->pipe_id = PIPE_RSVD; 31262306a36Sopenharmony_ci mutex_init(&stream->lock); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Register the ISR */ 31662306a36Sopenharmony_ci ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt, 31762306a36Sopenharmony_ci ctx->ops->irq_thread, 0, SST_DRV_NAME, 31862306a36Sopenharmony_ci ctx); 31962306a36Sopenharmony_ci if (ret) 32062306a36Sopenharmony_ci goto do_free_mem; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* default intr are unmasked so set this as masked */ 32562306a36Sopenharmony_ci sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ctx->qos = devm_kzalloc(ctx->dev, 32862306a36Sopenharmony_ci sizeof(struct pm_qos_request), GFP_KERNEL); 32962306a36Sopenharmony_ci if (!ctx->qos) { 33062306a36Sopenharmony_ci ret = -ENOMEM; 33162306a36Sopenharmony_ci goto do_free_mem; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name); 33662306a36Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name, 33762306a36Sopenharmony_ci ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); 33862306a36Sopenharmony_ci if (ret) { 33962306a36Sopenharmony_ci dev_err(ctx->dev, "Firmware download failed:%d\n", ret); 34062306a36Sopenharmony_ci goto do_free_mem; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = sysfs_create_group(&ctx->dev->kobj, 34462306a36Sopenharmony_ci &sst_fw_version_attr_group); 34562306a36Sopenharmony_ci if (ret) { 34662306a36Sopenharmony_ci dev_err(ctx->dev, 34762306a36Sopenharmony_ci "Unable to create sysfs\n"); 34862306a36Sopenharmony_ci goto err_sysfs; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci sst_register(ctx->dev); 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_cierr_sysfs: 35462306a36Sopenharmony_ci sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cido_free_mem: 35762306a36Sopenharmony_ci destroy_workqueue(ctx->post_msg_wq); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_init); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid sst_context_cleanup(struct intel_sst_drv *ctx) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci pm_runtime_get_noresume(ctx->dev); 36562306a36Sopenharmony_ci pm_runtime_disable(ctx->dev); 36662306a36Sopenharmony_ci sst_unregister(ctx->dev); 36762306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_SHUTDOWN); 36862306a36Sopenharmony_ci sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); 36962306a36Sopenharmony_ci destroy_workqueue(ctx->post_msg_wq); 37062306a36Sopenharmony_ci cpu_latency_qos_remove_request(ctx->qos); 37162306a36Sopenharmony_ci kfree(ctx->fw_sg_list.src); 37262306a36Sopenharmony_ci kfree(ctx->fw_sg_list.dst); 37362306a36Sopenharmony_ci ctx->fw_sg_list.list_len = 0; 37462306a36Sopenharmony_ci kfree(ctx->fw_in_mem); 37562306a36Sopenharmony_ci ctx->fw_in_mem = NULL; 37662306a36Sopenharmony_ci sst_memcpy_free_resources(ctx); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_context_cleanup); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_civoid sst_configure_runtime_pm(struct intel_sst_drv *ctx) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); 38362306a36Sopenharmony_ci pm_runtime_use_autosuspend(ctx->dev); 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * For acpi devices, the actual physical device state is 38662306a36Sopenharmony_ci * initially active. So change the state to active before 38762306a36Sopenharmony_ci * enabling the pm 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (!acpi_disabled) 39162306a36Sopenharmony_ci pm_runtime_set_active(ctx->dev); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci pm_runtime_enable(ctx->dev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (acpi_disabled) 39662306a36Sopenharmony_ci pm_runtime_set_active(ctx->dev); 39762306a36Sopenharmony_ci else 39862306a36Sopenharmony_ci pm_runtime_put_noidle(ctx->dev); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sst_configure_runtime_pm); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int intel_sst_runtime_suspend(struct device *dev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int ret = 0; 40562306a36Sopenharmony_ci struct intel_sst_drv *ctx = dev_get_drvdata(dev); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (ctx->sst_state == SST_RESET) { 40862306a36Sopenharmony_ci dev_dbg(dev, "LPE is already in RESET state, No action\n"); 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci /* save fw context */ 41262306a36Sopenharmony_ci if (ctx->ops->save_dsp_context(ctx)) 41362306a36Sopenharmony_ci return -EBUSY; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Move the SST state to Reset */ 41662306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_RESET); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci synchronize_irq(ctx->irq_num); 41962306a36Sopenharmony_ci flush_workqueue(ctx->post_msg_wq); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci ctx->ops->reset(ctx); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int intel_sst_suspend(struct device *dev) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct intel_sst_drv *ctx = dev_get_drvdata(dev); 42962306a36Sopenharmony_ci struct sst_fw_save *fw_save; 43062306a36Sopenharmony_ci int i, ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* check first if we are already in SW reset */ 43362306a36Sopenharmony_ci if (ctx->sst_state == SST_RESET) 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * check if any stream is active and running 43862306a36Sopenharmony_ci * they should already by suspend by soc_suspend 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci for (i = 1; i <= ctx->info.max_streams; i++) { 44162306a36Sopenharmony_ci struct stream_info *stream = &ctx->streams[i]; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (stream->status == STREAM_RUNNING) { 44462306a36Sopenharmony_ci dev_err(dev, "stream %d is running, can't suspend, abort\n", i); 44562306a36Sopenharmony_ci return -EBUSY; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (ctx->pdata->streams_lost_on_suspend) { 44962306a36Sopenharmony_ci stream->resume_status = stream->status; 45062306a36Sopenharmony_ci stream->resume_prev = stream->prev; 45162306a36Sopenharmony_ci if (stream->status != STREAM_UN_INIT) 45262306a36Sopenharmony_ci sst_free_stream(ctx, i); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci synchronize_irq(ctx->irq_num); 45662306a36Sopenharmony_ci flush_workqueue(ctx->post_msg_wq); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Move the SST state to Reset */ 45962306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_RESET); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* tell DSP we are suspending */ 46262306a36Sopenharmony_ci if (ctx->ops->save_dsp_context(ctx)) 46362306a36Sopenharmony_ci return -EBUSY; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* save the memories */ 46662306a36Sopenharmony_ci fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); 46762306a36Sopenharmony_ci if (!fw_save) 46862306a36Sopenharmony_ci return -ENOMEM; 46962306a36Sopenharmony_ci fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); 47062306a36Sopenharmony_ci if (!fw_save->iram) { 47162306a36Sopenharmony_ci ret = -ENOMEM; 47262306a36Sopenharmony_ci goto iram; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); 47562306a36Sopenharmony_ci if (!fw_save->dram) { 47662306a36Sopenharmony_ci ret = -ENOMEM; 47762306a36Sopenharmony_ci goto dram; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); 48062306a36Sopenharmony_ci if (!fw_save->sram) { 48162306a36Sopenharmony_ci ret = -ENOMEM; 48262306a36Sopenharmony_ci goto sram; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); 48662306a36Sopenharmony_ci if (!fw_save->ddr) { 48762306a36Sopenharmony_ci ret = -ENOMEM; 48862306a36Sopenharmony_ci goto ddr; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base); 49262306a36Sopenharmony_ci memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base); 49362306a36Sopenharmony_ci memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE); 49462306a36Sopenharmony_ci memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ctx->fw_save = fw_save; 49762306a36Sopenharmony_ci ctx->ops->reset(ctx); 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ciddr: 50062306a36Sopenharmony_ci kvfree(fw_save->sram); 50162306a36Sopenharmony_cisram: 50262306a36Sopenharmony_ci kvfree(fw_save->dram); 50362306a36Sopenharmony_cidram: 50462306a36Sopenharmony_ci kvfree(fw_save->iram); 50562306a36Sopenharmony_ciiram: 50662306a36Sopenharmony_ci kfree(fw_save); 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int intel_sst_resume(struct device *dev) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct intel_sst_drv *ctx = dev_get_drvdata(dev); 51362306a36Sopenharmony_ci struct sst_fw_save *fw_save = ctx->fw_save; 51462306a36Sopenharmony_ci struct sst_block *block; 51562306a36Sopenharmony_ci int i, ret = 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!fw_save) 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_FW_LOADING); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* we have to restore the memory saved */ 52362306a36Sopenharmony_ci ctx->ops->reset(ctx); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ctx->fw_save = NULL; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base); 52862306a36Sopenharmony_ci memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base); 52962306a36Sopenharmony_ci memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); 53062306a36Sopenharmony_ci memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci kvfree(fw_save->sram); 53362306a36Sopenharmony_ci kvfree(fw_save->dram); 53462306a36Sopenharmony_ci kvfree(fw_save->iram); 53562306a36Sopenharmony_ci kvfree(fw_save->ddr); 53662306a36Sopenharmony_ci kfree(fw_save); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci block = sst_create_block(ctx, 0, FW_DWNL_ID); 53962306a36Sopenharmony_ci if (block == NULL) 54062306a36Sopenharmony_ci return -ENOMEM; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* start and wait for ack */ 54462306a36Sopenharmony_ci ctx->ops->start(ctx); 54562306a36Sopenharmony_ci ret = sst_wait_timeout(ctx, block); 54662306a36Sopenharmony_ci if (ret) { 54762306a36Sopenharmony_ci dev_err(ctx->dev, "fw download failed %d\n", ret); 54862306a36Sopenharmony_ci /* FW download failed due to timeout */ 54962306a36Sopenharmony_ci ret = -EBUSY; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci } else { 55262306a36Sopenharmony_ci sst_set_fw_state_locked(ctx, SST_FW_RUNNING); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (ctx->pdata->streams_lost_on_suspend) { 55662306a36Sopenharmony_ci for (i = 1; i <= ctx->info.max_streams; i++) { 55762306a36Sopenharmony_ci struct stream_info *stream = &ctx->streams[i]; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (stream->resume_status != STREAM_UN_INIT) { 56062306a36Sopenharmony_ci dev_dbg(ctx->dev, "Re-allocing stream %d status %d prev %d\n", 56162306a36Sopenharmony_ci i, stream->resume_status, 56262306a36Sopenharmony_ci stream->resume_prev); 56362306a36Sopenharmony_ci sst_realloc_stream(ctx, i); 56462306a36Sopenharmony_ci stream->status = stream->resume_status; 56562306a36Sopenharmony_ci stream->prev = stream->resume_prev; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci sst_free_block(ctx, block); 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciconst struct dev_pm_ops intel_sst_pm = { 57562306a36Sopenharmony_ci .suspend = intel_sst_suspend, 57662306a36Sopenharmony_ci .resume = intel_sst_resume, 57762306a36Sopenharmony_ci .runtime_suspend = intel_sst_runtime_suspend, 57862306a36Sopenharmony_ci}; 57962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_sst_pm); 580