| /*************************************************************************** |
| * Copyright (c) 2005-2009, Broadcom Corporation. |
| * |
| * Name: crystalhd_cmds . c |
| * |
| * Description: |
| * BCM70010 Linux driver user command interfaces. |
| * |
| * HISTORY: |
| * |
| ********************************************************************** |
| * This file is part of the crystalhd device driver. |
| * |
| * This driver 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 driver 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 driver. If not, see <http://www.gnu.org/licenses/>. |
| **********************************************************************/ |
| |
| #include "crystalhd_cmds.h" |
| #include "crystalhd_hw.h" |
| |
| static struct crystalhd_user *bc_cproc_get_uid(struct crystalhd_cmd *ctx) |
| { |
| struct crystalhd_user *user = NULL; |
| int i; |
| |
| for (i = 0; i < BC_LINK_MAX_OPENS; i++) { |
| if (!ctx->user[i].in_use) { |
| user = &ctx->user[i]; |
| break; |
| } |
| } |
| |
| return user; |
| } |
| |
| static int bc_cproc_get_user_count(struct crystalhd_cmd *ctx) |
| { |
| int i, count = 0; |
| |
| for (i = 0; i < BC_LINK_MAX_OPENS; i++) { |
| if (ctx->user[i].in_use) |
| count++; |
| } |
| |
| return count; |
| } |
| |
| static void bc_cproc_mark_pwr_state(struct crystalhd_cmd *ctx) |
| { |
| int i; |
| |
| for (i = 0; i < BC_LINK_MAX_OPENS; i++) { |
| if (!ctx->user[i].in_use) |
| continue; |
| if (ctx->user[i].mode == DTS_DIAG_MODE || |
| ctx->user[i].mode == DTS_PLAYBACK_MODE) { |
| ctx->pwr_state_change = 1; |
| break; |
| } |
| } |
| } |
| |
| static enum BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| int rc = 0, i = 0; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (ctx->user[idata->u_id].mode != DTS_MODE_INV) { |
| BCMLOG_ERR("Close the handle first..\n"); |
| return BC_STS_ERR_USAGE; |
| } |
| if (idata->udata.u.NotifyMode.Mode == DTS_MONITOR_MODE) { |
| ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode; |
| return BC_STS_SUCCESS; |
| } |
| if (ctx->state != BC_LINK_INVALID) { |
| BCMLOG_ERR("Link invalid state %d\n", ctx->state); |
| return BC_STS_ERR_USAGE; |
| } |
| /* Check for duplicate playback sessions..*/ |
| for (i = 0; i < BC_LINK_MAX_OPENS; i++) { |
| if (ctx->user[i].mode == DTS_DIAG_MODE || |
| ctx->user[i].mode == DTS_PLAYBACK_MODE) { |
| BCMLOG_ERR("multiple playback sessions are not " |
| "supported..\n"); |
| return BC_STS_ERR_USAGE; |
| } |
| } |
| ctx->cin_wait_exit = 0; |
| ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode; |
| /* Setup mmap pool for uaddr sgl mapping..*/ |
| rc = crystalhd_create_dio_pool(ctx->adp, BC_LINK_MAX_SGLS); |
| if (rc) |
| return BC_STS_ERROR; |
| |
| /* Setup Hardware DMA rings */ |
| return crystalhd_hw_setup_dma_rings(&ctx->hw_ctx); |
| } |
| |
| static enum BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| idata->udata.u.VerInfo.DriverMajor = crystalhd_kmod_major; |
| idata->udata.u.VerInfo.DriverMinor = crystalhd_kmod_minor; |
| idata->udata.u.VerInfo.DriverRevision = crystalhd_kmod_rev; |
| return BC_STS_SUCCESS; |
| } |
| |
| |
| static enum BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| crystalhd_pci_cfg_rd(ctx->adp, 0, 2, |
| (uint32_t *)&idata->udata.u.hwType.PciVenId); |
| crystalhd_pci_cfg_rd(ctx->adp, 2, 2, |
| (uint32_t *)&idata->udata.u.hwType.PciDevId); |
| crystalhd_pci_cfg_rd(ctx->adp, 8, 1, |
| (uint32_t *)&idata->udata.u.hwType.HwRev); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| idata->udata.u.regAcc.Value = bc_dec_reg_rd(ctx->adp, |
| idata->udata.u.regAcc.Offset); |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| |
| bc_dec_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset, |
| idata->udata.u.regAcc.Value); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| |
| idata->udata.u.regAcc.Value = crystalhd_reg_rd(ctx->adp, |
| idata->udata.u.regAcc.Offset); |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| |
| crystalhd_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset, |
| idata->udata.u.regAcc.Value); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata || !idata->add_cdata) |
| return BC_STS_INV_ARG; |
| |
| if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) { |
| BCMLOG_ERR("insufficient buffer\n"); |
| return BC_STS_INV_ARG; |
| } |
| sts = crystalhd_mem_rd(ctx->adp, idata->udata.u.devMem.StartOff, |
| idata->udata.u.devMem.NumDwords, |
| (uint32_t *)idata->add_cdata); |
| return sts; |
| |
| } |
| |
| static enum BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata || !idata->add_cdata) |
| return BC_STS_INV_ARG; |
| |
| if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) { |
| BCMLOG_ERR("insufficient buffer\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| sts = crystalhd_mem_wr(ctx->adp, idata->udata.u.devMem.StartOff, |
| idata->udata.u.devMem.NumDwords, |
| (uint32_t *)idata->add_cdata); |
| return sts; |
| } |
| |
| static enum BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| uint32_t ix, cnt, off, len; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| uint32_t *temp; |
| |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| |
| temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space; |
| off = idata->udata.u.pciCfg.Offset; |
| len = idata->udata.u.pciCfg.Size; |
| |
| if (len <= 4) |
| return crystalhd_pci_cfg_rd(ctx->adp, off, len, temp); |
| |
| /* Truncate to dword alignment..*/ |
| len = 4; |
| cnt = idata->udata.u.pciCfg.Size / len; |
| for (ix = 0; ix < cnt; ix++) { |
| sts = crystalhd_pci_cfg_rd(ctx->adp, off, len, &temp[ix]); |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG_ERR("config read : %d\n", sts); |
| return sts; |
| } |
| off += len; |
| } |
| |
| return sts; |
| } |
| |
| static enum BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| uint32_t ix, cnt, off, len; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| uint32_t *temp; |
| |
| if (!ctx || !idata) |
| return BC_STS_INV_ARG; |
| |
| temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space; |
| off = idata->udata.u.pciCfg.Offset; |
| len = idata->udata.u.pciCfg.Size; |
| |
| if (len <= 4) |
| return crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[0]); |
| |
| /* Truncate to dword alignment..*/ |
| len = 4; |
| cnt = idata->udata.u.pciCfg.Size / len; |
| for (ix = 0; ix < cnt; ix++) { |
| sts = crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[ix]); |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG_ERR("config write : %d\n", sts); |
| return sts; |
| } |
| off += len; |
| } |
| |
| return sts; |
| } |
| |
| static enum BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata || !idata->add_cdata || !idata->add_cdata_sz) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (ctx->state != BC_LINK_INVALID) { |
| BCMLOG_ERR("Link invalid state %d\n", ctx->state); |
| return BC_STS_ERR_USAGE; |
| } |
| |
| sts = crystalhd_download_fw(ctx->adp, (uint8_t *)idata->add_cdata, |
| idata->add_cdata_sz); |
| |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG_ERR("Firmware Download Failure!! - %d\n", sts); |
| } else |
| ctx->state |= BC_LINK_INIT; |
| |
| return sts; |
| } |
| |
| /* |
| * We use the FW_CMD interface to sync up playback state with application |
| * and firmware. This function will perform the required pre and post |
| * processing of the Firmware commands. |
| * |
| * Pause - |
| * Disable capture after decoder pause. |
| * Resume - |
| * First enable capture and issue decoder resume command. |
| * Flush - |
| * Abort pending input transfers and issue decoder flush command. |
| * |
| */ |
| static enum BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| enum BC_STATUS sts; |
| uint32_t *cmd; |
| |
| if (!(ctx->state & BC_LINK_INIT)) { |
| BCMLOG_ERR("Link invalid state %d\n", ctx->state); |
| return BC_STS_ERR_USAGE; |
| } |
| |
| cmd = idata->udata.u.fwCmd.cmd; |
| |
| /* Pre-Process */ |
| if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) { |
| if (!cmd[3]) { |
| ctx->state &= ~BC_LINK_PAUSED; |
| crystalhd_hw_unpause(&ctx->hw_ctx); |
| } |
| } else if (cmd[0] == eCMD_C011_DEC_CHAN_FLUSH) { |
| BCMLOG(BCMLOG_INFO, "Flush issued\n"); |
| if (cmd[3]) |
| ctx->cin_wait_exit = 1; |
| } |
| |
| sts = crystalhd_do_fw_cmd(&ctx->hw_ctx, &idata->udata.u.fwCmd); |
| |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG(BCMLOG_INFO, "fw cmd %x failed\n", cmd[0]); |
| return sts; |
| } |
| |
| /* Post-Process */ |
| if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) { |
| if (cmd[3]) { |
| ctx->state |= BC_LINK_PAUSED; |
| crystalhd_hw_pause(&ctx->hw_ctx); |
| } |
| } |
| |
| return sts; |
| } |
| |
| static void bc_proc_in_completion(struct crystalhd_dio_req *dio_hnd, |
| wait_queue_head_t *event, enum BC_STATUS sts) |
| { |
| if (!dio_hnd || !event) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return; |
| } |
| if (sts == BC_STS_IO_USER_ABORT) |
| return; |
| |
| dio_hnd->uinfo.comp_sts = sts; |
| dio_hnd->uinfo.ev_sts = 1; |
| crystalhd_set_event(event); |
| } |
| |
| static enum BC_STATUS bc_cproc_codein_sleep(struct crystalhd_cmd *ctx) |
| { |
| wait_queue_head_t sleep_ev; |
| int rc = 0; |
| |
| if (ctx->state & BC_LINK_SUSPEND) |
| return BC_STS_IO_USER_ABORT; |
| |
| if (ctx->cin_wait_exit) { |
| ctx->cin_wait_exit = 0; |
| return BC_STS_CMD_CANCELLED; |
| } |
| crystalhd_create_event(&sleep_ev); |
| crystalhd_wait_on_event(&sleep_ev, 0, 100, rc, 0); |
| if (rc == -EINTR) |
| return BC_STS_IO_USER_ABORT; |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata, |
| struct crystalhd_dio_req *dio) |
| { |
| uint32_t tx_listid = 0; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| wait_queue_head_t event; |
| int rc = 0; |
| |
| if (!ctx || !idata || !dio) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| crystalhd_create_event(&event); |
| |
| ctx->tx_list_id = 0; |
| /* msleep_interruptible(2000); */ |
| sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, bc_proc_in_completion, |
| &event, &tx_listid, |
| idata->udata.u.ProcInput.Encrypted); |
| |
| while (sts == BC_STS_BUSY) { |
| sts = bc_cproc_codein_sleep(ctx); |
| if (sts != BC_STS_SUCCESS) |
| break; |
| sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, |
| bc_proc_in_completion, |
| &event, &tx_listid, |
| idata->udata.u.ProcInput.Encrypted); |
| } |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG(BCMLOG_DBG, "_hw_txdma returning sts:%d\n", sts); |
| return sts; |
| } |
| if (ctx->cin_wait_exit) |
| ctx->cin_wait_exit = 0; |
| |
| ctx->tx_list_id = tx_listid; |
| |
| /* _post() succeeded.. wait for the completion. */ |
| crystalhd_wait_on_event(&event, (dio->uinfo.ev_sts), 3000, rc, 0); |
| ctx->tx_list_id = 0; |
| if (!rc) { |
| return dio->uinfo.comp_sts; |
| } else if (rc == -EBUSY) { |
| BCMLOG(BCMLOG_DBG, "_tx_post() T/O\n"); |
| sts = BC_STS_TIMEOUT; |
| } else if (rc == -EINTR) { |
| BCMLOG(BCMLOG_DBG, "Tx Wait Signal int.\n"); |
| sts = BC_STS_IO_USER_ABORT; |
| } else { |
| sts = BC_STS_IO_ERROR; |
| } |
| |
| /* We are cancelling the IO from the same context as the _post(). |
| * so no need to wait on the event again.. the return itself |
| * ensures the release of our resources. |
| */ |
| crystalhd_hw_cancel_tx(&ctx->hw_ctx, tx_listid); |
| |
| return sts; |
| } |
| |
| /* Helper function to check on user buffers */ |
| static enum BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz, |
| uint32_t uv_off, bool en_422) |
| { |
| if (!ubuff || !ub_sz) { |
| BCMLOG_ERR("%s->Invalid Arg %p %x\n", |
| ((pin) ? "TX" : "RX"), ubuff, ub_sz); |
| return BC_STS_INV_ARG; |
| } |
| |
| /* Check for alignment */ |
| if (((uintptr_t)ubuff) & 0x03) { |
| BCMLOG_ERR("%s-->Un-aligned address not implemented yet.. %p\n", |
| ((pin) ? "TX" : "RX"), ubuff); |
| return BC_STS_NOT_IMPL; |
| } |
| if (pin) |
| return BC_STS_SUCCESS; |
| |
| if (!en_422 && !uv_off) { |
| BCMLOG_ERR("Need UV offset for 420 mode.\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (en_422 && uv_off) { |
| BCMLOG_ERR("UV offset in 422 mode ??\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| void *ubuff; |
| uint32_t ub_sz; |
| struct crystalhd_dio_req *dio_hnd = NULL; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| ubuff = idata->udata.u.ProcInput.pDmaBuff; |
| ub_sz = idata->udata.u.ProcInput.BuffSz; |
| |
| sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| |
| sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd); |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG_ERR("dio map - %d\n", sts); |
| return sts; |
| } |
| |
| if (!dio_hnd) |
| return BC_STS_ERROR; |
| |
| sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd); |
| |
| crystalhd_unmap_dio(ctx->adp, dio_hnd); |
| |
| return sts; |
| } |
| |
| static enum BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| void *ubuff; |
| uint32_t ub_sz, uv_off; |
| bool en_422; |
| struct crystalhd_dio_req *dio_hnd = NULL; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| ubuff = idata->udata.u.RxBuffs.YuvBuff; |
| ub_sz = idata->udata.u.RxBuffs.YuvBuffSz; |
| uv_off = idata->udata.u.RxBuffs.UVbuffOffset; |
| en_422 = idata->udata.u.RxBuffs.b422Mode; |
| |
| sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| |
| sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off, |
| en_422, 0, &dio_hnd); |
| if (sts != BC_STS_SUCCESS) { |
| BCMLOG_ERR("dio map - %d\n", sts); |
| return sts; |
| } |
| |
| if (!dio_hnd) |
| return BC_STS_ERROR; |
| |
| sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY)); |
| if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) { |
| crystalhd_unmap_dio(ctx->adp, dio_hnd); |
| return sts; |
| } |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx, |
| struct crystalhd_dio_req *dio) |
| { |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| |
| ctx->state |= BC_LINK_FMT_CHG; |
| if (ctx->state == BC_LINK_READY) |
| sts = crystalhd_hw_start_capture(&ctx->hw_ctx); |
| |
| return sts; |
| } |
| |
| static enum BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| struct crystalhd_dio_req *dio = NULL; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| struct BC_DEC_OUT_BUFF *frame; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (!(ctx->state & BC_LINK_CAP_EN)) { |
| BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state); |
| return BC_STS_ERR_USAGE; |
| } |
| |
| frame = &idata->udata.u.DecOutData; |
| |
| sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio); |
| if (sts != BC_STS_SUCCESS) |
| return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts; |
| |
| frame->Flags = dio->uinfo.comp_flags; |
| |
| if (frame->Flags & COMP_FLAG_FMT_CHANGE) |
| return bc_cproc_fmt_change(ctx, dio); |
| |
| frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff; |
| frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len; |
| frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset; |
| frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode; |
| |
| frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz; |
| frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz; |
| |
| crystalhd_unmap_dio(ctx->adp, dio); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| ctx->state |= BC_LINK_CAP_EN; |
| if (ctx->state == BC_LINK_READY) |
| return crystalhd_hw_start_capture(&ctx->hw_ctx); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| struct crystalhd_dio_req *dio = NULL; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| struct BC_DEC_OUT_BUFF *frame; |
| uint32_t count; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (!(ctx->state & BC_LINK_CAP_EN)) |
| return BC_STS_ERR_USAGE; |
| |
| /* We should ack flush even when we are in paused/suspend state */ |
| if (!(ctx->state & BC_LINK_READY)) |
| return crystalhd_hw_stop_capture(&ctx->hw_ctx); |
| |
| ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG); |
| |
| frame = &idata->udata.u.DecOutData; |
| for (count = 0; count < BC_RX_LIST_CNT; count++) { |
| |
| sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio); |
| if (sts != BC_STS_SUCCESS) |
| break; |
| |
| crystalhd_unmap_dio(ctx->adp, dio); |
| } |
| |
| return crystalhd_hw_stop_capture(&ctx->hw_ctx); |
| } |
| |
| static enum BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| struct BC_DTS_STATS *stats; |
| struct crystalhd_hw_stats hw_stats; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats); |
| |
| stats = &idata->udata.u.drvStat; |
| stats->drvRLL = hw_stats.rdyq_count; |
| stats->drvFLL = hw_stats.freeq_count; |
| stats->DrvTotalFrmDropped = hw_stats.rx_errors; |
| stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors; |
| stats->intCount = hw_stats.num_interrupts; |
| stats->DrvIgnIntrCnt = hw_stats.num_interrupts - |
| hw_stats.dev_interrupts; |
| stats->TxFifoBsyCnt = hw_stats.cin_busy; |
| stats->pauseCount = hw_stats.pause_cnt; |
| |
| if (ctx->pwr_state_change) |
| stats->pwr_state_change = 1; |
| if (ctx->state & BC_LINK_PAUSED) |
| stats->DrvPauseTime = 1; |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| crystalhd_hw_stats(&ctx->hw_ctx, NULL); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| static enum BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| struct BC_CLOCK *clock; |
| uint32_t oldClk; |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| clock = &idata->udata.u.clockValue; |
| oldClk = ctx->hw_ctx.core_clock_mhz; |
| ctx->hw_ctx.core_clock_mhz = clock->clk; |
| |
| if (ctx->state & BC_LINK_READY) { |
| sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx); |
| if (sts == BC_STS_CLK_NOCHG) |
| ctx->hw_ctx.core_clock_mhz = oldClk; |
| } |
| |
| clock->clk = ctx->hw_ctx.core_clock_mhz; |
| |
| return sts; |
| } |
| |
| /*=============== Cmd Proc Table.. ======================================*/ |
| static const struct crystalhd_cmd_tbl g_crystalhd_cproc_tbl[] = { |
| { BCM_IOC_GET_VERSION, bc_cproc_get_version, 0}, |
| { BCM_IOC_GET_HWTYPE, bc_cproc_get_hwtype, 0}, |
| { BCM_IOC_REG_RD, bc_cproc_reg_rd, 0}, |
| { BCM_IOC_REG_WR, bc_cproc_reg_wr, 0}, |
| { BCM_IOC_FPGA_RD, bc_cproc_link_reg_rd, 0}, |
| { BCM_IOC_FPGA_WR, bc_cproc_link_reg_wr, 0}, |
| { BCM_IOC_MEM_RD, bc_cproc_mem_rd, 0}, |
| { BCM_IOC_MEM_WR, bc_cproc_mem_wr, 0}, |
| { BCM_IOC_RD_PCI_CFG, bc_cproc_cfg_rd, 0}, |
| { BCM_IOC_WR_PCI_CFG, bc_cproc_cfg_wr, 1}, |
| { BCM_IOC_FW_DOWNLOAD, bc_cproc_download_fw, 1}, |
| { BCM_IOC_FW_CMD, bc_cproc_do_fw_cmd, 1}, |
| { BCM_IOC_PROC_INPUT, bc_cproc_proc_input, 1}, |
| { BCM_IOC_ADD_RXBUFFS, bc_cproc_add_cap_buff, 1}, |
| { BCM_IOC_FETCH_RXBUFF, bc_cproc_fetch_frame, 1}, |
| { BCM_IOC_START_RX_CAP, bc_cproc_start_capture, 1}, |
| { BCM_IOC_FLUSH_RX_CAP, bc_cproc_flush_cap_buffs, 1}, |
| { BCM_IOC_GET_DRV_STAT, bc_cproc_get_stats, 0}, |
| { BCM_IOC_RST_DRV_STAT, bc_cproc_reset_stats, 0}, |
| { BCM_IOC_NOTIFY_MODE, bc_cproc_notify_mode, 0}, |
| { BCM_IOC_CHG_CLK, bc_cproc_chg_clk, 0}, |
| { BCM_IOC_END, NULL}, |
| }; |
| |
| /*=============== Cmd Proc Functions.. ===================================*/ |
| |
| /** |
| * crystalhd_suspend - Power management suspend request. |
| * @ctx: Command layer context. |
| * @idata: Iodata - required for internal use. |
| * |
| * Return: |
| * status |
| * |
| * 1. Set the state to Suspend. |
| * 2. Flush the Rx Buffers it will unmap all the buffers and |
| * stop the RxDMA engine. |
| * 3. Cancel The TX Io and Stop Dma Engine. |
| * 4. Put the DDR in to deep sleep. |
| * 5. Stop the hardware putting it in to Reset State. |
| * |
| * Current gstreamer frame work does not provide any power management |
| * related notification to user mode decoder plug-in. As a work-around |
| * we pass on the power mangement notification to our plug-in by completing |
| * all outstanding requests with BC_STS_IO_USER_ABORT return code. |
| */ |
| enum BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, |
| struct crystalhd_ioctl_data *idata) |
| { |
| enum BC_STATUS sts = BC_STS_SUCCESS; |
| |
| if (!ctx || !idata) { |
| BCMLOG_ERR("Invalid Parameters\n"); |
| return BC_STS_ERROR; |
| } |
| |
| if (ctx->state & BC_LINK_SUSPEND) |
| return BC_STS_SUCCESS; |
| |
| if (ctx->state == BC_LINK_INVALID) { |
| BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n"); |
| return BC_STS_SUCCESS; |
| } |
| |
| ctx->state |= BC_LINK_SUSPEND; |
| |
| bc_cproc_mark_pwr_state(ctx); |
| |
| if (ctx->state & BC_LINK_CAP_EN) { |
| sts = bc_cproc_flush_cap_buffs(ctx, idata); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| } |
| |
| if (ctx->tx_list_id) { |
| sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| } |
| |
| sts = crystalhd_hw_suspend(&ctx->hw_ctx); |
| if (sts != BC_STS_SUCCESS) |
| return sts; |
| |
| BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n"); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_resume - Resume frame capture. |
| * @ctx: Command layer contextx. |
| * |
| * Return: |
| * status |
| * |
| * |
| * Resume frame capture. |
| * |
| * PM_Resume can't resume the playback state back to pre-suspend state |
| * because we don't keep video clip related information within driver. |
| * To get back to the pre-suspend state App will re-open the device and |
| * start a new playback session from the pre-suspend clip position. |
| * |
| */ |
| enum BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx) |
| { |
| BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state); |
| |
| bc_cproc_mark_pwr_state(ctx); |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_user_open - Create application handle. |
| * @ctx: Command layer contextx. |
| * @user_ctx: User ID context. |
| * |
| * Return: |
| * status |
| * |
| * Creates an application specific UID and allocates |
| * application specific resources. HW layer initialization |
| * is done for the first open request. |
| */ |
| enum BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx, |
| struct crystalhd_user **user_ctx) |
| { |
| struct crystalhd_user *uc; |
| |
| if (!ctx || !user_ctx) { |
| BCMLOG_ERR("Invalid arg..\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| uc = bc_cproc_get_uid(ctx); |
| if (!uc) { |
| BCMLOG(BCMLOG_INFO, "No free user context...\n"); |
| return BC_STS_BUSY; |
| } |
| |
| BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid); |
| |
| crystalhd_hw_open(&ctx->hw_ctx, ctx->adp); |
| |
| uc->in_use = 1; |
| |
| *user_ctx = uc; |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_user_close - Close application handle. |
| * @ctx: Command layer contextx. |
| * @uc: User ID context. |
| * |
| * Return: |
| * status |
| * |
| * Closer aplication handle and release app specific |
| * resources. |
| */ |
| enum BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc) |
| { |
| uint32_t mode = uc->mode; |
| |
| ctx->user[uc->uid].mode = DTS_MODE_INV; |
| ctx->user[uc->uid].in_use = 0; |
| ctx->cin_wait_exit = 1; |
| ctx->pwr_state_change = 0; |
| |
| BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid); |
| |
| if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) { |
| crystalhd_hw_free_dma_rings(&ctx->hw_ctx); |
| crystalhd_destroy_dio_pool(ctx->adp); |
| } else if (bc_cproc_get_user_count(ctx)) { |
| return BC_STS_SUCCESS; |
| } |
| |
| crystalhd_hw_close(&ctx->hw_ctx); |
| |
| ctx->state = BC_LINK_INVALID; |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_setup_cmd_context - Setup Command layer resources. |
| * @ctx: Command layer contextx. |
| * @adp: Adapter context |
| * |
| * Return: |
| * status |
| * |
| * Called at the time of driver load. |
| */ |
| enum BC_STATUS __devinit crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx, |
| struct crystalhd_adp *adp) |
| { |
| int i = 0; |
| |
| if (!ctx || !adp) { |
| BCMLOG_ERR("Invalid arg!!\n"); |
| return BC_STS_INV_ARG; |
| } |
| |
| if (ctx->adp) |
| BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n"); |
| |
| ctx->adp = adp; |
| for (i = 0; i < BC_LINK_MAX_OPENS; i++) { |
| ctx->user[i].uid = i; |
| ctx->user[i].in_use = 0; |
| ctx->user[i].mode = DTS_MODE_INV; |
| } |
| |
| /*Open and Close the Hardware to put it in to sleep state*/ |
| crystalhd_hw_open(&ctx->hw_ctx, ctx->adp); |
| crystalhd_hw_close(&ctx->hw_ctx); |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_delete_cmd_context - Release Command layer resources. |
| * @ctx: Command layer contextx. |
| * |
| * Return: |
| * status |
| * |
| * Called at the time of driver un-load. |
| */ |
| enum BC_STATUS __devexit crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx) |
| { |
| BCMLOG(BCMLOG_DBG, "Deleting Command context..\n"); |
| |
| ctx->adp = NULL; |
| |
| return BC_STS_SUCCESS; |
| } |
| |
| /** |
| * crystalhd_get_cmd_proc - Cproc table lookup. |
| * @ctx: Command layer contextx. |
| * @cmd: IOCTL command code. |
| * @uc: User ID context. |
| * |
| * Return: |
| * command proc function pointer |
| * |
| * This function checks the process context, application's |
| * mode of operation and returns the function pointer |
| * from the cproc table. |
| */ |
| crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd, |
| struct crystalhd_user *uc) |
| { |
| crystalhd_cmd_proc cproc = NULL; |
| unsigned int i, tbl_sz; |
| |
| if (!ctx) { |
| BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd); |
| return NULL; |
| } |
| |
| if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) { |
| BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd); |
| return NULL; |
| } |
| |
| tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(struct crystalhd_cmd_tbl); |
| for (i = 0; i < tbl_sz; i++) { |
| if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) { |
| if ((uc->mode == DTS_MONITOR_MODE) && |
| (g_crystalhd_cproc_tbl[i].block_mon)) { |
| BCMLOG(BCMLOG_INFO, "Blocking cmd %d\n", cmd); |
| break; |
| } |
| cproc = g_crystalhd_cproc_tbl[i].cmd_proc; |
| break; |
| } |
| } |
| |
| return cproc; |
| } |
| |
| /** |
| * crystalhd_cmd_interrupt - ISR entry point |
| * @ctx: Command layer contextx. |
| * |
| * Return: |
| * TRUE: If interrupt from bcm70012 device. |
| * |
| * |
| * ISR entry point from OS layer. |
| */ |
| bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx) |
| { |
| if (!ctx) { |
| BCMLOG_ERR("Invalid arg..\n"); |
| return 0; |
| } |
| |
| return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx); |
| } |