void free_stream_context(unsigned int str_id) { struct stream_info *stream; if (!sst_validate_strid(str_id)) { /* str_id is valid, so stream is alloacted */ stream = &sst_drv_ctx->streams[str_id]; if (sst_free_stream(str_id)) sst_clean_stream(&sst_drv_ctx->streams[str_id]); if (stream->ops == STREAM_OPS_PLAYBACK || stream->ops == STREAM_OPS_PLAYBACK_DRM) { sst_drv_ctx->pb_streams--; if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) sst_drv_ctx->scard_ops->power_down_pmic_pb( stream->device); else { if (sst_drv_ctx->pb_streams == 0) sst_drv_ctx->scard_ops-> power_down_pmic_pb(stream->device); } } else if (stream->ops == STREAM_OPS_CAPTURE) { sst_drv_ctx->cp_streams--; if (sst_drv_ctx->cp_streams == 0) sst_drv_ctx->scard_ops->power_down_pmic_cp( stream->device); } if (sst_drv_ctx->pb_streams == 0 && sst_drv_ctx->cp_streams == 0) sst_drv_ctx->scard_ops->power_down_pmic(); } }
/* * sst_close_pcm_stream - Close PCM interface * * @str_id: stream id to be closed * * This function is called by MID sound card driver to close * an existing pcm interface */ int sst_close_pcm_stream(unsigned int str_id) { struct stream_info *stream; pr_debug("sst: stream free called\n"); if (sst_validate_strid(str_id)) return -EINVAL; stream = &sst_drv_ctx->streams[str_id]; free_stream_context(str_id); stream->pcm_substream = NULL; stream->status = STREAM_UN_INIT; stream->period_elapsed = NULL; sst_drv_ctx->stream_cnt--; pr_debug("sst: will call runtime put now\n"); pm_runtime_put(&sst_drv_ctx->pci->dev); return 0; }
/** * 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(" 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_ALG_PARAMS: { pr_debug("sst:IPC_ALG_PARAMS response %x\n", msg->header.full); pr_debug("sst: data value %x\n", msg->header.part.data); pr_debug("sst: large value %x\n", msg->header.part.large); if (!msg->header.part.large) { if (!msg->header.part.data) { pr_debug("sst: alg set success\n"); sst_drv_ctx->ppp_params_blk.ret_code = 0; } else { pr_debug("sst: alg set failed\n"); sst_drv_ctx->ppp_params_blk.ret_code = -msg->header.part.data; } } else if (msg->header.part.data) { struct snd_ppp_params *mailbox_params, *get_params; char *params; pr_debug("sst: alg get success\n"); mailbox_params = (struct snd_ppp_params *)msg->mailbox; get_params = kzalloc(sizeof(*get_params), GFP_KERNEL); if (get_params == NULL) { pr_err("sst: out of memory for ALG PARAMS"); break; } memcpy_fromio(get_params, mailbox_params, sizeof(*get_params)); get_params->params = kzalloc(mailbox_params->size, GFP_KERNEL); if (get_params->params == NULL) { kfree(get_params); pr_err("sst: out of memory for ALG PARAMS block"); break; } params = msg->mailbox; params = params + sizeof(*mailbox_params) - sizeof(u32); memcpy_fromio(get_params->params, params, get_params->size); sst_drv_ctx->ppp_params_blk.ret_code = 0; sst_drv_ctx->ppp_params_blk.data = get_params; } if (sst_drv_ctx->ppp_params_blk.on == true) { sst_drv_ctx->ppp_params_blk.condition = true; wake_up(&sst_drv_ctx->wait_queue); } break; } case IPC_IA_TUNING_PARAMS: { pr_debug("sst:IPC_TUNING_PARAMS resp: %x\n", msg->header.full); pr_debug("data value %x\n", msg->header.part.data); if (msg->header.part.large) { pr_debug("alg set failed\n"); sst_drv_ctx->ppp_params_blk.ret_code = -msg->header.part.data; } else { pr_debug("alg set success\n"); sst_drv_ctx->ppp_params_blk.ret_code = 0; } if (sst_drv_ctx->ppp_params_blk.on == true) { sst_drv_ctx->ppp_params_blk.condition = true; wake_up(&sst_drv_ctx->wait_queue); } } 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("Msg succeeded %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(" 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("Memcopy succeeded\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("Msg succeeded %x\n", msg->header.part.msg_id); sst_drv_ctx->mute_info_blk.ret_code = 0; } else { pr_err(" 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("Msg succeeded %x\n", msg->header.part.msg_id); sst_drv_ctx->vol_info_blk.ret_code = 0; } else { pr_err(" 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("Large Msg Received Successfully\n"); pr_debug("Msg succeeded %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("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("stream id %d invalid\n", str_id); break; } str_info = &sst_drv_ctx->streams[str_id]; if (msg->header.part.large) { pr_debug("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("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("stream id %d invalid\n", str_id); break; } str_info = &sst_drv_ctx->streams[str_id]; if (msg->header.part.large) { pr_debug("Msg succeeded %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("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("stream id %d invalid\n", str_id); break; } str_info = &sst_drv_ctx->streams[str_id]; if (!msg->header.part.data) { pr_debug("Msg succeeded %x\n", msg->header.part.msg_id); str_info->ctrl_blk.ret_code = 0; } else { pr_err(" 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("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("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(" 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("RX_TIME_SLOT success\n"); sst_drv_ctx->hs_info_blk.ret_code = 0; } else { pr_err(" 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("Msg succeeded %x\n", msg->header.part.msg_id); str_info->ctrl_blk.ret_code = 0; } else { pr_err(" 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(" 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: str_info = &sst_drv_ctx->streams[str_id]; if (!msg->header.part.data) { pr_debug("Stream %d freed\n", str_id); } else { pr_err("Free for %d ret error %x\n", str_id, 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_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("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("stream id %d invalid\n", str_id); break; } pr_debug("Ack for play/capt frames received\n"); break; case IPC_IA_PREP_LIB_DNLD: { struct snd_sst_str_type *str_type = (struct snd_sst_str_type *)msg->mailbox; pr_debug("Prep Lib download %x\n", msg->header.part.msg_id); if (str_type->result) pr_err("Prep lib download %x\n", str_type->result); else pr_debug("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("Lib downloaded %x\n", msg->header.part.msg_id); if (resp->result) { pr_err("err in lib dload %x\n", resp->result); } else { pr_debug("Codec download complete...\n"); pr_debug("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("Build date:%sTime:%s", build->date, build->time); break; } case IPC_IA_SET_PMIC_TYPE: break; case IPC_IA_START_STREAM: pr_debug("reply for START STREAM %x\n", msg->header.full); break; case IPC_IA_GET_FW_CTXT: pr_debug("reply for get fw ctxt %x\n", msg->header.full); if (msg->header.part.data) sst_drv_ctx->fw_cntx_size = 0; else sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx; pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size); sst_wake_up_alloc_block( sst_drv_ctx, str_id, msg->header.part.data, NULL); break; default: /* Illegal case */ pr_err("process reply:default = %x\n", msg->header.full); } sst_clear_interrupt(); return; }
/** * 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("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("stream id %d invalid\n", str_id); break; } pr_err("Buffer under/overrun for %d\n", msg->header.part.str_id); pr_err("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("strid %d invalid\n", str_id); break; } /* call sst_play_frame */ stream = &sst_drv_ctx->streams[str_id]; pr_debug("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_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("str id %d invalid\n", str_id); break; } stream = &sst_drv_ctx->streams[str_id]; pr_debug("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("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_play_frames for Penwell!!\n"); break; case IPC_IA_PRINT_STRING: pr_debug("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("stream id %d invalid\n", str_id); break; } pr_err("codec fatal error %x stream %d...\n", msg->header.full, msg->header.part.str_id); pr_err("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("Unhandled msg %x header %x\n", msg->header.part.msg_id, msg->header.full); } sst_clear_interrupt(); return; }
/* * sst_device_control - Set Control params * * @cmd: control cmd to be set * @arg: command argument * * This function is called by MID sound card driver to set * SST/Sound card controls for an opened stream. * This is registered with MID driver */ int sst_device_control(int cmd, void *arg) { int retval = 0, str_id = 0; switch (cmd) { case SST_SND_PAUSE: case SST_SND_RESUME: case SST_SND_DROP: case SST_SND_START: sst_drv_ctx->mad_ops.control_op = cmd; sst_drv_ctx->mad_ops.stream_id = *(int *)arg; queue_work(sst_drv_ctx->mad_wq, &sst_drv_ctx->mad_ops.wq); break; case SST_SND_STREAM_INIT: { struct pcm_stream_info *str_info; struct stream_info *stream; pr_debug("stream init called\n"); str_info = (struct pcm_stream_info *)arg; str_id = str_info->str_id; retval = sst_validate_strid(str_id); if (retval) break; stream = &sst_drv_ctx->streams[str_id]; pr_debug("setting the period ptrs\n"); stream->pcm_substream = str_info->mad_substream; stream->period_elapsed = str_info->period_elapsed; stream->sfreq = str_info->sfreq; stream->prev = stream->status; stream->status = STREAM_INIT; break; } case SST_SND_BUFFER_POINTER: { struct pcm_stream_info *stream_info; struct snd_sst_tstamp fw_tstamp = {0,}; struct stream_info *stream; stream_info = (struct pcm_stream_info *)arg; str_id = stream_info->str_id; retval = sst_validate_strid(str_id); if (retval) break; stream = &sst_drv_ctx->streams[str_id]; if (!stream->pcm_substream) break; memcpy_fromio(&fw_tstamp, ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) +(str_id * sizeof(fw_tstamp))), sizeof(fw_tstamp)); pr_debug("Pointer Query on strid = %d ops %d\n", str_id, stream->ops); if (stream->ops == STREAM_OPS_PLAYBACK) stream_info->buffer_ptr = fw_tstamp.samples_rendered; else stream_info->buffer_ptr = fw_tstamp.samples_processed; pr_debug("Samples rendered = %llu, buffer ptr %llu\n", fw_tstamp.samples_rendered, stream_info->buffer_ptr); break; } case SST_ENABLE_RX_TIME_SLOT: { int status = *(int *)arg; sst_drv_ctx->rx_time_slot_status = status ; sst_enable_rx_timeslot(status); break; } default: /* Illegal case */ pr_warn("illegal req\n"); return -EINVAL; } return retval; }