Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  sst_ipc.c - Intel SST Driver for audio engine
0004  *
0005  *  Copyright (C) 2008-14 Intel Corporation
0006  *  Authors:    Vinod Koul <vinod.koul@intel.com>
0007  *      Harsha Priya <priya.harsha@intel.com>
0008  *      Dharageswari R <dharageswari.r@intel.com>
0009  *      KP Jeeja <jeeja.kp@intel.com>
0010  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0011  *
0012  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0013  */
0014 #include <linux/pci.h>
0015 #include <linux/firmware.h>
0016 #include <linux/sched.h>
0017 #include <linux/delay.h>
0018 #include <linux/pm_runtime.h>
0019 #include <sound/core.h>
0020 #include <sound/pcm.h>
0021 #include <sound/soc.h>
0022 #include <sound/compress_driver.h>
0023 #include <asm/intel-mid.h>
0024 #include <asm/platform_sst_audio.h>
0025 #include "../sst-mfld-platform.h"
0026 #include "sst.h"
0027 
0028 struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
0029                     u32 msg_id, u32 drv_id)
0030 {
0031     struct sst_block *msg;
0032 
0033     dev_dbg(ctx->dev, "Enter\n");
0034     msg = kzalloc(sizeof(*msg), GFP_KERNEL);
0035     if (!msg)
0036         return NULL;
0037     msg->condition = false;
0038     msg->on = true;
0039     msg->msg_id = msg_id;
0040     msg->drv_id = drv_id;
0041     spin_lock_bh(&ctx->block_lock);
0042     list_add_tail(&msg->node, &ctx->block_list);
0043     spin_unlock_bh(&ctx->block_lock);
0044 
0045     return msg;
0046 }
0047 
0048 /*
0049  * while handling the interrupts, we need to check for message status and
0050  * then if we are blocking for a message
0051  *
0052  * here we are unblocking the blocked ones, this is based on id we have
0053  * passed and search that for block threads.
0054  * We will not find block in two cases
0055  *  a) when its small message and block in not there, so silently ignore
0056  *  them
0057  *  b) when we are actually not able to find the block (bug perhaps)
0058  *
0059  *  Since we have bit of small messages we can spam kernel log with err
0060  *  print on above so need to keep as debug prints which should be enabled
0061  *  via dynamic debug while debugging IPC issues
0062  */
0063 int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
0064         u32 drv_id, u32 ipc, void *data, u32 size)
0065 {
0066     struct sst_block *block;
0067 
0068     dev_dbg(ctx->dev, "Enter\n");
0069 
0070     spin_lock_bh(&ctx->block_lock);
0071     list_for_each_entry(block, &ctx->block_list, node) {
0072         dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
0073                             block->drv_id);
0074         if (block->msg_id == ipc && block->drv_id == drv_id) {
0075             dev_dbg(ctx->dev, "free up the block\n");
0076             block->ret_code = result;
0077             block->data = data;
0078             block->size = size;
0079             block->condition = true;
0080             spin_unlock_bh(&ctx->block_lock);
0081             wake_up(&ctx->wait_queue);
0082             return 0;
0083         }
0084     }
0085     spin_unlock_bh(&ctx->block_lock);
0086     dev_dbg(ctx->dev,
0087         "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
0088         ipc, drv_id);
0089     return -EINVAL;
0090 }
0091 
0092 int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
0093 {
0094     struct sst_block *block, *__block;
0095 
0096     dev_dbg(ctx->dev, "Enter\n");
0097     spin_lock_bh(&ctx->block_lock);
0098     list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
0099         if (block == freed) {
0100             pr_debug("pvt_id freed --> %d\n", freed->drv_id);
0101             /* toggle the index position of pvt_id */
0102             list_del(&freed->node);
0103             spin_unlock_bh(&ctx->block_lock);
0104             kfree(freed->data);
0105             freed->data = NULL;
0106             kfree(freed);
0107             return 0;
0108         }
0109     }
0110     spin_unlock_bh(&ctx->block_lock);
0111     dev_err(ctx->dev, "block is already freed!!!\n");
0112     return -EINVAL;
0113 }
0114 
0115 int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
0116         struct ipc_post *ipc_msg, bool sync)
0117 {
0118     struct ipc_post *msg = ipc_msg;
0119     union ipc_header_mrfld header;
0120     unsigned int loop_count = 0;
0121     int retval = 0;
0122     unsigned long irq_flags;
0123 
0124     dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
0125     spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0126     header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
0127     if (sync) {
0128         while (header.p.header_high.part.busy) {
0129             if (loop_count > 25) {
0130                 dev_err(sst_drv_ctx->dev,
0131                     "sst: Busy wait failed, can't send this msg\n");
0132                 retval = -EBUSY;
0133                 goto out;
0134             }
0135             cpu_relax();
0136             loop_count++;
0137             header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
0138         }
0139     } else {
0140         if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
0141             /* queue is empty, nothing to send */
0142             spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0143             dev_dbg(sst_drv_ctx->dev,
0144                     "Empty msg queue... NO Action\n");
0145             return 0;
0146         }
0147 
0148         if (header.p.header_high.part.busy) {
0149             spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0150             dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
0151             return 0;
0152         }
0153 
0154         /* copy msg from list */
0155         msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
0156                 struct ipc_post, node);
0157         list_del(&msg->node);
0158     }
0159     dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
0160                 msg->mrfld_header.p.header_high.full);
0161     dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
0162             msg->mrfld_header.p.header_low_payload);
0163 
0164     if (msg->mrfld_header.p.header_high.part.large)
0165         memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
0166             msg->mailbox_data,
0167             msg->mrfld_header.p.header_low_payload);
0168 
0169     sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
0170 
0171 out:
0172     spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0173     kfree(msg->mailbox_data);
0174     kfree(msg);
0175     return retval;
0176 }
0177 
0178 void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
0179 {
0180     union interrupt_reg_mrfld isr;
0181     union interrupt_reg_mrfld imr;
0182     union ipc_header_mrfld clear_ipc;
0183     unsigned long irq_flags;
0184 
0185     spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0186     imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
0187     isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
0188 
0189     /* write 1 to clear*/
0190     isr.part.busy_interrupt = 1;
0191     sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
0192 
0193     /* Set IA done bit */
0194     clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
0195 
0196     clear_ipc.p.header_high.part.busy = 0;
0197     clear_ipc.p.header_high.part.done = 1;
0198     clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
0199     sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
0200     /* un mask busy interrupt */
0201     imr.part.busy_interrupt = 0;
0202     sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
0203     spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
0204 }
0205 
0206 
0207 /*
0208  * process_fw_init - process the FW init msg
0209  *
0210  * @msg: IPC message mailbox data from FW
0211  *
0212  * This function processes the FW init msg from FW
0213  * marks FW state and prints debug info of loaded FW
0214  */
0215 static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
0216             void *msg)
0217 {
0218     struct ipc_header_fw_init *init =
0219         (struct ipc_header_fw_init *)msg;
0220     int retval = 0;
0221 
0222     dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
0223     if (init->result) {
0224         sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
0225         dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
0226                 init->result);
0227         retval = init->result;
0228         goto ret;
0229     }
0230     if (memcmp(&sst_drv_ctx->fw_version, &init->fw_version,
0231            sizeof(init->fw_version)))
0232         dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
0233             init->fw_version.type, init->fw_version.major,
0234             init->fw_version.minor, init->fw_version.build);
0235     dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
0236             init->build_info.date, init->build_info.time);
0237 
0238     /* Save FW version */
0239     sst_drv_ctx->fw_version.type = init->fw_version.type;
0240     sst_drv_ctx->fw_version.major = init->fw_version.major;
0241     sst_drv_ctx->fw_version.minor = init->fw_version.minor;
0242     sst_drv_ctx->fw_version.build = init->fw_version.build;
0243 
0244 ret:
0245     sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
0246 }
0247 
0248 static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
0249             struct ipc_post *msg)
0250 {
0251     u32 msg_id;
0252     int str_id;
0253     u32 data_size, i;
0254     void *data_offset;
0255     struct stream_info *stream;
0256     u32 msg_low, pipe_id;
0257 
0258     msg_low = msg->mrfld_header.p.header_low_payload;
0259     msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
0260     data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
0261     data_size =  msg_low - (sizeof(struct ipc_dsp_hdr));
0262 
0263     switch (msg_id) {
0264     case IPC_SST_PERIOD_ELAPSED_MRFLD:
0265         pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
0266         str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
0267         if (str_id > 0) {
0268             dev_dbg(sst_drv_ctx->dev,
0269                 "Period elapsed rcvd for pipe id 0x%x\n",
0270                 pipe_id);
0271             stream = &sst_drv_ctx->streams[str_id];
0272             /* If stream is dropped, skip processing this message*/
0273             if (stream->status == STREAM_INIT)
0274                 break;
0275             if (stream->period_elapsed)
0276                 stream->period_elapsed(stream->pcm_substream);
0277             if (stream->compr_cb)
0278                 stream->compr_cb(stream->compr_cb_param);
0279         }
0280         break;
0281 
0282     case IPC_IA_DRAIN_STREAM_MRFLD:
0283         pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
0284         str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
0285         if (str_id > 0) {
0286             stream = &sst_drv_ctx->streams[str_id];
0287             if (stream->drain_notify)
0288                 stream->drain_notify(stream->drain_cb_param);
0289         }
0290         break;
0291 
0292     case IPC_IA_FW_ASYNC_ERR_MRFLD:
0293         dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
0294         for (i = 0; i < (data_size/4); i++)
0295             print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
0296                     16, 4, data_offset, data_size, false);
0297         break;
0298 
0299     case IPC_IA_FW_INIT_CMPLT_MRFLD:
0300         process_fw_init(sst_drv_ctx, data_offset);
0301         break;
0302 
0303     case IPC_IA_BUF_UNDER_RUN_MRFLD:
0304         pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
0305         str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
0306         if (str_id > 0)
0307             dev_err(sst_drv_ctx->dev,
0308                 "Buffer under-run for pipe:%#x str_id:%d\n",
0309                 pipe_id, str_id);
0310         break;
0311 
0312     default:
0313         dev_err(sst_drv_ctx->dev,
0314             "Unrecognized async msg from FW msg_id %#x\n", msg_id);
0315     }
0316 }
0317 
0318 void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
0319         struct ipc_post *msg)
0320 {
0321     unsigned int drv_id;
0322     void *data;
0323     union ipc_header_high msg_high;
0324     u32 msg_low;
0325     struct ipc_dsp_hdr *dsp_hdr;
0326 
0327     msg_high = msg->mrfld_header.p.header_high;
0328     msg_low = msg->mrfld_header.p.header_low_payload;
0329 
0330     dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
0331             msg->mrfld_header.p.header_high.full,
0332             msg->mrfld_header.p.header_low_payload);
0333 
0334     drv_id = msg_high.part.drv_id;
0335 
0336     /* Check for async messages first */
0337     if (drv_id == SST_ASYNC_DRV_ID) {
0338         /*FW sent async large message*/
0339         process_fw_async_msg(sst_drv_ctx, msg);
0340         return;
0341     }
0342 
0343     /* FW sent short error response for an IPC */
0344     if (msg_high.part.result && !msg_high.part.large) {
0345         /* 32-bit FW error code in msg_low */
0346         dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
0347         sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
0348             msg_high.part.drv_id,
0349             msg_high.part.msg_id, NULL, 0);
0350         return;
0351     }
0352 
0353     /*
0354      * Process all valid responses
0355      * if it is a large message, the payload contains the size to
0356      * copy from mailbox
0357      **/
0358     if (msg_high.part.large) {
0359         data = kmemdup((void *)msg->mailbox_data, msg_low, GFP_KERNEL);
0360         if (!data)
0361             return;
0362         /* Copy command id so that we can use to put sst to reset */
0363         dsp_hdr = (struct ipc_dsp_hdr *)data;
0364         dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
0365         if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
0366                 msg_high.part.drv_id,
0367                 msg_high.part.msg_id, data, msg_low))
0368             kfree(data);
0369     } else {
0370         sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
0371                 msg_high.part.drv_id,
0372                 msg_high.part.msg_id, NULL, 0);
0373     }
0374 
0375 }