| /* |
| * intel_sst_ipc.c - Intel SST Driver for audio engine |
| * |
| * Copyright (C) 2008-10 Intel Corporation |
| * Authors: Vinod Koul <vinod.koul@intel.com> |
| * Harsha Priya <priya.harsha@intel.com> |
| * Dharageswari R <dharageswari.r@intel.com> |
| * KP Jeeja <jeeja.kp@intel.com> |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
| * |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| * |
| * This file defines all ipc functions |
| */ |
| |
| #include <linux/pci.h> |
| #include <linux/firmware.h> |
| #include <linux/sched.h> |
| #include "intel_sst.h" |
| #include "intel_sst_ioctl.h" |
| #include "intel_sst_fw_ipc.h" |
| #include "intel_sst_common.h" |
| |
| /* |
| * sst_send_sound_card_type - send sound card type |
| * |
| * this function sends the sound card type to sst dsp engine |
| */ |
| static void sst_send_sound_card_type(void) |
| { |
| struct ipc_post *msg = NULL; |
| |
| if (sst_create_short_msg(&msg)) |
| return; |
| |
| sst_fill_header(&msg->header, IPC_IA_SET_PMIC_TYPE, 0, 0); |
| msg->header.part.data = sst_drv_ctx->pmic_vendor; |
| spin_lock(&sst_drv_ctx->list_spin_lock); |
| list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); |
| spin_unlock(&sst_drv_ctx->list_spin_lock); |
| sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); |
| return; |
| } |
| |
| /** |
| * sst_post_message - Posts message to SST |
| * |
| * @work: Pointer to work structure |
| * |
| * This function is called by any component in driver which |
| * wants to send an IPC message. This will post message only if |
| * busy bit is free |
| */ |
| void sst_post_message(struct work_struct *work) |
| { |
| struct ipc_post *msg; |
| union ipc_header header; |
| union interrupt_reg imr; |
| int retval = 0; |
| imr.full = 0; |
| |
| /*To check if LPE is in stalled state.*/ |
| retval = sst_stalled(); |
| if (retval < 0) { |
| pr_err("sst: in stalled state\n"); |
| return; |
| } |
| pr_debug("sst: post message called\n"); |
| spin_lock(&sst_drv_ctx->list_spin_lock); |
| |
| /* check list */ |
| if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { |
| /* list is empty, mask imr */ |
| pr_debug("sst: Empty msg queue... masking\n"); |
| imr.full = readl(sst_drv_ctx->shim + SST_IMRX); |
| imr.part.done_interrupt = 1; |
| /* dummy register for shim workaround */ |
| sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); |
| spin_unlock(&sst_drv_ctx->list_spin_lock); |
| return; |
| } |
| |
| /* check busy bit */ |
| header.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCX); |
| if (header.part.busy) { |
| /* busy, unmask */ |
| pr_debug("sst: Busy not free... unmasking\n"); |
| imr.full = readl(sst_drv_ctx->shim + SST_IMRX); |
| imr.part.done_interrupt = 0; |
| /* dummy register for shim workaround */ |
| sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); |
| spin_unlock(&sst_drv_ctx->list_spin_lock); |
| return; |
| } |
| /* copy msg from list */ |
| msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, |
| struct ipc_post, node); |
| list_del(&msg->node); |
| pr_debug("sst: Post message: header = %x\n", msg->header.full); |
| pr_debug("sst: size: = %x\n", msg->header.part.data); |
| if (msg->header.part.large) |
| memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, |
| msg->mailbox_data, msg->header.part.data); |
| /* dummy register for shim workaround */ |
| |
| sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header.full); |
| spin_unlock(&sst_drv_ctx->list_spin_lock); |
| |
| kfree(msg->mailbox_data); |
| kfree(msg); |
| return; |
| } |
| |
| /* |
| * sst_clear_interrupt - clear the SST FW interrupt |
| * |
| * This function clears the interrupt register after the interrupt |
| * bottom half is complete allowing next interrupt to arrive |
| */ |
| void sst_clear_interrupt(void) |
| { |
| union interrupt_reg isr; |
| union interrupt_reg imr; |
| union ipc_header clear_ipc; |
| |
| imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX); |
| isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX); |
| /* write 1 to clear */; |
| isr.part.busy_interrupt = 1; |
| sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); |
| /* Set IA done bit */ |
| clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCD); |
| clear_ipc.part.busy = 0; |
| clear_ipc.part.done = 1; |
| clear_ipc.part.data = IPC_ACK_SUCCESS; |
| sst_shim_write(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); |
| /* un mask busy interrupt */ |
| imr.part.busy_interrupt = 0; |
| sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); |
| } |
| |
| /* |
| * process_fw_init - process the FW init msg |
| * |
| * @msg: IPC message from FW |
| * |
| * This function processes the FW init msg from FW |
| * marks FW state and prints debug info of loaded FW |
| */ |
| int process_fw_init(struct sst_ipc_msg_wq *msg) |
| { |
| struct ipc_header_fw_init *init = |
| (struct ipc_header_fw_init *)msg->mailbox; |
| int retval = 0; |
| |
| pr_debug("sst: *** FW Init msg came***\n"); |
| if (init->result) { |
| mutex_lock(&sst_drv_ctx->sst_lock); |
| sst_drv_ctx->sst_state = SST_ERROR; |
| mutex_unlock(&sst_drv_ctx->sst_lock); |
| pr_debug("sst: FW Init failed, Error %x\n", init->result); |
| pr_err("sst: FW Init failed, Error %x\n", init->result); |
| retval = -init->result; |
| return retval; |
| } |
| if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) |
| sst_send_sound_card_type(); |
| mutex_lock(&sst_drv_ctx->sst_lock); |
| sst_drv_ctx->sst_state = SST_FW_RUNNING; |
| mutex_unlock(&sst_drv_ctx->sst_lock); |
| pr_debug("sst: FW Version %x.%x\n", |
| init->fw_version.major, init->fw_version.minor); |
| pr_debug("sst: Build No %x Type %x\n", |
| init->fw_version.build, init->fw_version.type); |
| pr_debug("sst: Build date %s Time %s\n", |
| init->build_info.date, init->build_info.time); |
| sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL); |
| return retval; |
| } |
| /** |
| * sst_process_message - Processes message from SST |
| * |
| * @work: Pointer to work structure |
| * |
| * This function is scheduled by ISR |
| * It take a msg from process_queue and does action based on msg |
| */ |
| void sst_process_message(struct work_struct *work) |
| { |
| struct sst_ipc_msg_wq *msg = |
| container_of(work, struct sst_ipc_msg_wq, wq); |
| int str_id = msg->header.part.str_id; |
| |
| pr_debug("sst: IPC process for %x\n", msg->header.full); |
| |
| /* based on msg in list call respective handler */ |
| switch (msg->header.part.msg_id) { |
| case IPC_SST_BUF_UNDER_RUN: |
| case IPC_SST_BUF_OVER_RUN: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| pr_err("sst: Buffer under/overrun for%d\n", |
| msg->header.part.str_id); |
| pr_err("sst: Got Underrun & not to send data...ignore\n"); |
| break; |
| |
| case IPC_SST_GET_PLAY_FRAMES: |
| if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { |
| struct stream_info *stream ; |
| |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: strid %d invalid\n", str_id); |
| break; |
| } |
| /* call sst_play_frame */ |
| stream = &sst_drv_ctx->streams[str_id]; |
| pr_debug("sst: sst_play_frames for %d\n", |
| msg->header.part.str_id); |
| mutex_lock(&sst_drv_ctx->streams[str_id].lock); |
| sst_play_frame(msg->header.part.str_id); |
| mutex_unlock(&sst_drv_ctx->streams[str_id].lock); |
| break; |
| } else |
| pr_err("sst: sst_play_frames for Penwell!!\n"); |
| |
| case IPC_SST_GET_CAPT_FRAMES: |
| if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { |
| struct stream_info *stream; |
| /* call sst_capture_frame */ |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: str id %d invalid\n", str_id); |
| break; |
| } |
| stream = &sst_drv_ctx->streams[str_id]; |
| pr_debug("sst: sst_capture_frames for %d\n", |
| msg->header.part.str_id); |
| mutex_lock(&stream->lock); |
| if (stream->mmapped == false && |
| stream->src == SST_DRV) { |
| pr_debug("sst: waking up block for copy.\n"); |
| stream->data_blk.ret_code = 0; |
| stream->data_blk.condition = true; |
| stream->data_blk.on = false; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } else |
| sst_capture_frame(msg->header.part.str_id); |
| mutex_unlock(&stream->lock); |
| } else |
| pr_err("sst: sst_play_frames for Penwell!!\n"); |
| break; |
| |
| case IPC_IA_PRINT_STRING: |
| pr_debug("sst: been asked to print something by fw\n"); |
| /* TBD */ |
| break; |
| |
| case IPC_IA_FW_INIT_CMPLT: { |
| /* send next data to FW */ |
| process_fw_init(msg); |
| break; |
| } |
| |
| case IPC_SST_STREAM_PROCESS_FATAL_ERR: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| pr_err("sst: codec fatal error %x stream %d...\n", |
| msg->header.full, msg->header.part.str_id); |
| pr_err("sst: Dropping the stream\n"); |
| sst_drop_stream(msg->header.part.str_id); |
| break; |
| case IPC_IA_LPE_GETTING_STALLED: |
| sst_drv_ctx->lpe_stalled = 1; |
| break; |
| case IPC_IA_LPE_UNSTALLED: |
| sst_drv_ctx->lpe_stalled = 0; |
| break; |
| default: |
| /* Illegal case */ |
| pr_err("sst: Unhandled msg %x header %x\n", |
| msg->header.part.msg_id, msg->header.full); |
| } |
| sst_clear_interrupt(); |
| return; |
| } |
| |
| /** |
| * sst_process_reply - Processes reply message from SST |
| * |
| * @work: Pointer to work structure |
| * |
| * This function is scheduled by ISR |
| * It take a reply msg from response_queue and |
| * does action based on msg |
| */ |
| void sst_process_reply(struct work_struct *work) |
| { |
| struct sst_ipc_msg_wq *msg = |
| container_of(work, struct sst_ipc_msg_wq, wq); |
| |
| int str_id = msg->header.part.str_id; |
| struct stream_info *str_info; |
| |
| switch (msg->header.part.msg_id) { |
| case IPC_IA_TARGET_DEV_SELECT: |
| if (!msg->header.part.data) { |
| sst_drv_ctx->tgt_dev_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| sst_drv_ctx->tgt_dev_blk.ret_code = |
| -msg->header.part.data; |
| } |
| |
| if (sst_drv_ctx->tgt_dev_blk.on == true) { |
| sst_drv_ctx->tgt_dev_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_GET_FW_INFO: { |
| struct snd_sst_fw_info *fw_info = |
| (struct snd_sst_fw_info *)msg->mailbox; |
| if (msg->header.part.large) { |
| int major = fw_info->fw_version.major; |
| int minor = fw_info->fw_version.minor; |
| int build = fw_info->fw_version.build; |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| pr_debug("INFO: ***FW*** = %02d.%02d.%02d\n", |
| major, minor, build); |
| memcpy_fromio(sst_drv_ctx->fw_info_blk.data, |
| ((struct snd_sst_fw_info *)(msg->mailbox)), |
| sizeof(struct snd_sst_fw_info)); |
| sst_drv_ctx->fw_info_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| sst_drv_ctx->fw_info_blk.ret_code = |
| -msg->header.part.data; |
| } |
| if (sst_drv_ctx->fw_info_blk.on == true) { |
| pr_debug("sst: Memcopy succedded\n"); |
| sst_drv_ctx->fw_info_blk.on = false; |
| sst_drv_ctx->fw_info_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| } |
| case IPC_IA_SET_STREAM_MUTE: |
| if (!msg->header.part.data) { |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| sst_drv_ctx->mute_info_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| sst_drv_ctx->mute_info_blk.ret_code = |
| -msg->header.part.data; |
| |
| } |
| if (sst_drv_ctx->mute_info_blk.on == true) { |
| sst_drv_ctx->mute_info_blk.on = false; |
| sst_drv_ctx->mute_info_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_SET_STREAM_VOL: |
| if (!msg->header.part.data) { |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| sst_drv_ctx->vol_info_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, |
| msg->header.part.data); |
| sst_drv_ctx->vol_info_blk.ret_code = |
| -msg->header.part.data; |
| |
| } |
| |
| if (sst_drv_ctx->vol_info_blk.on == true) { |
| sst_drv_ctx->vol_info_blk.on = false; |
| sst_drv_ctx->vol_info_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_GET_STREAM_VOL: |
| if (msg->header.part.large) { |
| pr_debug("sst: Large Msg Received Successfully\n"); |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| memcpy_fromio(sst_drv_ctx->vol_info_blk.data, |
| (void *) msg->mailbox, |
| sizeof(struct snd_sst_vol)); |
| sst_drv_ctx->vol_info_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| sst_drv_ctx->vol_info_blk.ret_code = |
| -msg->header.part.data; |
| } |
| if (sst_drv_ctx->vol_info_blk.on == true) { |
| sst_drv_ctx->vol_info_blk.on = false; |
| sst_drv_ctx->vol_info_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| |
| case IPC_IA_GET_STREAM_PARAMS: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (msg->header.part.large) { |
| pr_debug("sst: Get stream large success\n"); |
| memcpy_fromio(str_info->ctrl_blk.data, |
| ((void *)(msg->mailbox)), |
| sizeof(struct snd_sst_fw_get_stream_params)); |
| str_info->ctrl_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| str_info->ctrl_blk.ret_code = -msg->header.part.data; |
| } |
| if (str_info->ctrl_blk.on == true) { |
| str_info->ctrl_blk.on = false; |
| str_info->ctrl_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_DECODE_FRAMES: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (msg->header.part.large) { |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| memcpy_fromio(str_info->data_blk.data, |
| ((void *)(msg->mailbox)), |
| sizeof(struct snd_sst_decode_info)); |
| str_info->data_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| str_info->data_blk.ret_code = -msg->header.part.data; |
| } |
| if (str_info->data_blk.on == true) { |
| str_info->data_blk.on = false; |
| str_info->data_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_DRAIN_STREAM: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (!msg->header.part.data) { |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| str_info->ctrl_blk.ret_code = 0; |
| |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| str_info->ctrl_blk.ret_code = -msg->header.part.data; |
| |
| } |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (str_info->data_blk.on == true) { |
| str_info->data_blk.on = false; |
| str_info->data_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| |
| case IPC_IA_DROP_STREAM: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: str id %d invalid\n", str_id); |
| break; |
| } |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (msg->header.part.large) { |
| struct snd_sst_drop_response *drop_resp = |
| (struct snd_sst_drop_response *)msg->mailbox; |
| |
| pr_debug("sst: Drop ret bytes %x\n", drop_resp->bytes); |
| |
| str_info->curr_bytes = drop_resp->bytes; |
| str_info->ctrl_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, msg->header.part.data); |
| str_info->ctrl_blk.ret_code = -msg->header.part.data; |
| } |
| if (str_info->ctrl_blk.on == true) { |
| str_info->ctrl_blk.on = false; |
| str_info->ctrl_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_ENABLE_RX_TIME_SLOT: |
| if (!msg->header.part.data) { |
| pr_debug("sst: RX_TIME_SLOT success\n"); |
| sst_drv_ctx->hs_info_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, |
| msg->header.part.data); |
| sst_drv_ctx->hs_info_blk.ret_code = |
| -msg->header.part.data; |
| } |
| if (sst_drv_ctx->hs_info_blk.on == true) { |
| sst_drv_ctx->hs_info_blk.on = false; |
| sst_drv_ctx->hs_info_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| case IPC_IA_PAUSE_STREAM: |
| case IPC_IA_RESUME_STREAM: |
| case IPC_IA_SET_STREAM_PARAMS: |
| str_info = &sst_drv_ctx->streams[str_id]; |
| if (!msg->header.part.data) { |
| pr_debug("sst: Msg succedded %x\n", |
| msg->header.part.msg_id); |
| str_info->ctrl_blk.ret_code = 0; |
| } else { |
| pr_err("sst: Msg %x reply error %x\n", |
| msg->header.part.msg_id, |
| msg->header.part.data); |
| str_info->ctrl_blk.ret_code = -msg->header.part.data; |
| } |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n", str_id); |
| break; |
| } |
| |
| if (str_info->ctrl_blk.on == true) { |
| str_info->ctrl_blk.on = false; |
| str_info->ctrl_blk.condition = true; |
| wake_up(&sst_drv_ctx->wait_queue); |
| } |
| break; |
| |
| case IPC_IA_FREE_STREAM: |
| if (!msg->header.part.data) { |
| pr_debug("sst: Stream %d freed\n", str_id); |
| } else { |
| pr_err("sst: Free for %d ret error %x\n", |
| str_id, msg->header.part.data); |
| } |
| break; |
| case IPC_IA_ALLOC_STREAM: { |
| /* map to stream, call play */ |
| struct snd_sst_alloc_response *resp = |
| (struct snd_sst_alloc_response *)msg->mailbox; |
| if (resp->str_type.result) |
| pr_err("sst: error alloc stream = %x\n", |
| resp->str_type.result); |
| sst_alloc_stream_response(str_id, resp); |
| break; |
| } |
| |
| case IPC_IA_PLAY_FRAMES: |
| case IPC_IA_CAPT_FRAMES: |
| if (sst_validate_strid(str_id)) { |
| pr_err("sst: stream id %d invalid\n" , str_id); |
| break; |
| } |
| pr_debug("sst: Ack for play/capt frames recived\n"); |
| break; |
| |
| case IPC_IA_PREP_LIB_DNLD: { |
| struct snd_sst_str_type *str_type = |
| (struct snd_sst_str_type *)msg->mailbox; |
| pr_debug("sst: Prep Lib download %x\n", |
| msg->header.part.msg_id); |
| if (str_type->result) |
| pr_err("sst: Prep lib download %x\n", str_type->result); |
| else |
| pr_debug("sst: Can download codec now...\n"); |
| sst_wake_up_alloc_block(sst_drv_ctx, str_id, |
| str_type->result, NULL); |
| break; |
| } |
| |
| case IPC_IA_LIB_DNLD_CMPLT: { |
| struct snd_sst_lib_download_info *resp = |
| (struct snd_sst_lib_download_info *)msg->mailbox; |
| int retval = resp->result; |
| |
| pr_debug("sst: Lib downloaded %x\n", msg->header.part.msg_id); |
| if (resp->result) { |
| pr_err("sst: err in lib dload %x\n", resp->result); |
| } else { |
| pr_debug("sst: Codec download complete...\n"); |
| pr_debug("sst: codec Type %d Ver %d Built %s: %s\n", |
| resp->dload_lib.lib_info.lib_type, |
| resp->dload_lib.lib_info.lib_version, |
| resp->dload_lib.lib_info.b_date, |
| resp->dload_lib.lib_info.b_time); |
| } |
| sst_wake_up_alloc_block(sst_drv_ctx, str_id, |
| retval, NULL); |
| break; |
| } |
| |
| case IPC_IA_GET_FW_VERSION: { |
| struct ipc_header_fw_init *version = |
| (struct ipc_header_fw_init *)msg->mailbox; |
| int major = version->fw_version.major; |
| int minor = version->fw_version.minor; |
| int build = version->fw_version.build; |
| dev_info(&sst_drv_ctx->pci->dev, |
| "INFO: ***LOADED SST FW VERSION*** = %02d.%02d.%02d\n", |
| major, minor, build); |
| break; |
| } |
| case IPC_IA_GET_FW_BUILD_INF: { |
| struct sst_fw_build_info *build = |
| (struct sst_fw_build_info *)msg->mailbox; |
| pr_debug("sst: Build date:%sTime:%s", build->date, build->time); |
| break; |
| } |
| case IPC_IA_SET_PMIC_TYPE: |
| break; |
| case IPC_IA_START_STREAM: |
| pr_debug("sst: reply for START STREAM %x\n", msg->header.full); |
| break; |
| default: |
| /* Illegal case */ |
| pr_err("sst: process reply:default = %x\n", msg->header.full); |
| } |
| sst_clear_interrupt(); |
| return; |
| } |