linux/drivers/staging/crystalhd/crystalhd_cmds.c
<<
>>
Prefs
   1/***************************************************************************
   2 * Copyright (c) 2005-2009, Broadcom Corporation.
   3 *
   4 *  Name: crystalhd_cmds . c
   5 *
   6 *  Description:
   7 *              BCM70010 Linux driver user command interfaces.
   8 *
   9 *  HISTORY:
  10 *
  11 **********************************************************************
  12 * This file is part of the crystalhd device driver.
  13 *
  14 * This driver is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation, version 2 of the License.
  17 *
  18 * This driver is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this driver.  If not, see <http://www.gnu.org/licenses/>.
  25 **********************************************************************/
  26
  27#include "crystalhd_cmds.h"
  28#include "crystalhd_hw.h"
  29
  30static struct crystalhd_user *bc_cproc_get_uid(struct crystalhd_cmd *ctx)
  31{
  32        struct crystalhd_user *user = NULL;
  33        int i;
  34
  35        for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
  36                if (!ctx->user[i].in_use) {
  37                        user = &ctx->user[i];
  38                        break;
  39                }
  40        }
  41
  42        return user;
  43}
  44
  45static int bc_cproc_get_user_count(struct crystalhd_cmd *ctx)
  46{
  47        int i, count = 0;
  48
  49        for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
  50                if (ctx->user[i].in_use)
  51                        count++;
  52        }
  53
  54        return count;
  55}
  56
  57static void bc_cproc_mark_pwr_state(struct crystalhd_cmd *ctx)
  58{
  59        int i;
  60
  61        for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
  62                if (!ctx->user[i].in_use)
  63                        continue;
  64                if (ctx->user[i].mode == DTS_DIAG_MODE ||
  65                    ctx->user[i].mode == DTS_PLAYBACK_MODE) {
  66                        ctx->pwr_state_change = 1;
  67                        break;
  68                }
  69        }
  70}
  71
  72static enum BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx,
  73                                      struct crystalhd_ioctl_data *idata)
  74{
  75        int rc = 0, i = 0;
  76
  77        if (!ctx || !idata) {
  78                BCMLOG_ERR("Invalid Arg!!\n");
  79                return BC_STS_INV_ARG;
  80        }
  81
  82        if (ctx->user[idata->u_id].mode != DTS_MODE_INV) {
  83                BCMLOG_ERR("Close the handle first..\n");
  84                return BC_STS_ERR_USAGE;
  85        }
  86        if (idata->udata.u.NotifyMode.Mode == DTS_MONITOR_MODE) {
  87                ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
  88                return BC_STS_SUCCESS;
  89        }
  90        if (ctx->state != BC_LINK_INVALID) {
  91                BCMLOG_ERR("Link invalid state %d\n", ctx->state);
  92                return BC_STS_ERR_USAGE;
  93        }
  94        /* Check for duplicate playback sessions..*/
  95        for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
  96                if (ctx->user[i].mode == DTS_DIAG_MODE ||
  97                    ctx->user[i].mode == DTS_PLAYBACK_MODE) {
  98                        BCMLOG_ERR("multiple playback sessions are not "
  99                                   "supported..\n");
 100                        return BC_STS_ERR_USAGE;
 101                }
 102        }
 103        ctx->cin_wait_exit = 0;
 104        ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
 105        /* Setup mmap pool for uaddr sgl mapping..*/
 106        rc = crystalhd_create_dio_pool(ctx->adp, BC_LINK_MAX_SGLS);
 107        if (rc)
 108                return BC_STS_ERROR;
 109
 110        /* Setup Hardware DMA rings */
 111        return crystalhd_hw_setup_dma_rings(&ctx->hw_ctx);
 112}
 113
 114static enum BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx,
 115                                      struct crystalhd_ioctl_data *idata)
 116{
 117
 118        if (!ctx || !idata) {
 119                BCMLOG_ERR("Invalid Arg!!\n");
 120                return BC_STS_INV_ARG;
 121        }
 122        idata->udata.u.VerInfo.DriverMajor = crystalhd_kmod_major;
 123        idata->udata.u.VerInfo.DriverMinor = crystalhd_kmod_minor;
 124        idata->udata.u.VerInfo.DriverRevision   = crystalhd_kmod_rev;
 125        return BC_STS_SUCCESS;
 126}
 127
 128
 129static enum BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx,
 130                                        struct crystalhd_ioctl_data *idata)
 131{
 132        if (!ctx || !idata) {
 133                BCMLOG_ERR("Invalid Arg!!\n");
 134                return BC_STS_INV_ARG;
 135        }
 136
 137        crystalhd_pci_cfg_rd(ctx->adp, 0, 2,
 138                           (uint32_t *)&idata->udata.u.hwType.PciVenId);
 139        crystalhd_pci_cfg_rd(ctx->adp, 2, 2,
 140                           (uint32_t *)&idata->udata.u.hwType.PciDevId);
 141        crystalhd_pci_cfg_rd(ctx->adp, 8, 1,
 142                           (uint32_t *)&idata->udata.u.hwType.HwRev);
 143
 144        return BC_STS_SUCCESS;
 145}
 146
 147static enum BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx,
 148                                 struct crystalhd_ioctl_data *idata)
 149{
 150        if (!ctx || !idata)
 151                return BC_STS_INV_ARG;
 152        idata->udata.u.regAcc.Value = bc_dec_reg_rd(ctx->adp,
 153                                        idata->udata.u.regAcc.Offset);
 154        return BC_STS_SUCCESS;
 155}
 156
 157static enum BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx,
 158                                 struct crystalhd_ioctl_data *idata)
 159{
 160        if (!ctx || !idata)
 161                return BC_STS_INV_ARG;
 162
 163        bc_dec_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
 164                      idata->udata.u.regAcc.Value);
 165
 166        return BC_STS_SUCCESS;
 167}
 168
 169static enum BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx,
 170                                      struct crystalhd_ioctl_data *idata)
 171{
 172        if (!ctx || !idata)
 173                return BC_STS_INV_ARG;
 174
 175        idata->udata.u.regAcc.Value = crystalhd_reg_rd(ctx->adp,
 176                                        idata->udata.u.regAcc.Offset);
 177        return BC_STS_SUCCESS;
 178}
 179
 180static enum BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx,
 181                                      struct crystalhd_ioctl_data *idata)
 182{
 183        if (!ctx || !idata)
 184                return BC_STS_INV_ARG;
 185
 186        crystalhd_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
 187                       idata->udata.u.regAcc.Value);
 188
 189        return BC_STS_SUCCESS;
 190}
 191
 192static enum BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx,
 193                                 struct crystalhd_ioctl_data *idata)
 194{
 195        enum BC_STATUS sts = BC_STS_SUCCESS;
 196
 197        if (!ctx || !idata || !idata->add_cdata)
 198                return BC_STS_INV_ARG;
 199
 200        if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
 201                BCMLOG_ERR("insufficient buffer\n");
 202                return BC_STS_INV_ARG;
 203        }
 204        sts = crystalhd_mem_rd(ctx->adp, idata->udata.u.devMem.StartOff,
 205                             idata->udata.u.devMem.NumDwords,
 206                             (uint32_t *)idata->add_cdata);
 207        return sts;
 208
 209}
 210
 211static enum BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx,
 212                                 struct crystalhd_ioctl_data *idata)
 213{
 214        enum BC_STATUS sts = BC_STS_SUCCESS;
 215
 216        if (!ctx || !idata || !idata->add_cdata)
 217                return BC_STS_INV_ARG;
 218
 219        if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
 220                BCMLOG_ERR("insufficient buffer\n");
 221                return BC_STS_INV_ARG;
 222        }
 223
 224        sts = crystalhd_mem_wr(ctx->adp, idata->udata.u.devMem.StartOff,
 225                             idata->udata.u.devMem.NumDwords,
 226                             (uint32_t *)idata->add_cdata);
 227        return sts;
 228}
 229
 230static enum BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx,
 231                                 struct crystalhd_ioctl_data *idata)
 232{
 233        uint32_t ix, cnt, off, len;
 234        enum BC_STATUS sts = BC_STS_SUCCESS;
 235        uint32_t *temp;
 236
 237        if (!ctx || !idata)
 238                return BC_STS_INV_ARG;
 239
 240        temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
 241        off = idata->udata.u.pciCfg.Offset;
 242        len = idata->udata.u.pciCfg.Size;
 243
 244        if (len <= 4)
 245                return crystalhd_pci_cfg_rd(ctx->adp, off, len, temp);
 246
 247        /* Truncate to dword alignment..*/
 248        len = 4;
 249        cnt = idata->udata.u.pciCfg.Size / len;
 250        for (ix = 0; ix < cnt; ix++) {
 251                sts = crystalhd_pci_cfg_rd(ctx->adp, off, len, &temp[ix]);
 252                if (sts != BC_STS_SUCCESS) {
 253                        BCMLOG_ERR("config read : %d\n", sts);
 254                        return sts;
 255                }
 256                off += len;
 257        }
 258
 259        return sts;
 260}
 261
 262static enum BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx,
 263                                 struct crystalhd_ioctl_data *idata)
 264{
 265        uint32_t ix, cnt, off, len;
 266        enum BC_STATUS sts = BC_STS_SUCCESS;
 267        uint32_t *temp;
 268
 269        if (!ctx || !idata)
 270                return BC_STS_INV_ARG;
 271
 272        temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
 273        off = idata->udata.u.pciCfg.Offset;
 274        len = idata->udata.u.pciCfg.Size;
 275
 276        if (len <= 4)
 277                return crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[0]);
 278
 279        /* Truncate to dword alignment..*/
 280        len = 4;
 281        cnt = idata->udata.u.pciCfg.Size / len;
 282        for (ix = 0; ix < cnt; ix++) {
 283                sts = crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[ix]);
 284                if (sts != BC_STS_SUCCESS) {
 285                        BCMLOG_ERR("config write : %d\n", sts);
 286                        return sts;
 287                }
 288                off += len;
 289        }
 290
 291        return sts;
 292}
 293
 294static enum BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx,
 295                                      struct crystalhd_ioctl_data *idata)
 296{
 297        enum BC_STATUS sts = BC_STS_SUCCESS;
 298
 299        if (!ctx || !idata || !idata->add_cdata || !idata->add_cdata_sz) {
 300                BCMLOG_ERR("Invalid Arg!!\n");
 301                return BC_STS_INV_ARG;
 302        }
 303
 304        if (ctx->state != BC_LINK_INVALID) {
 305                BCMLOG_ERR("Link invalid state %d\n", ctx->state);
 306                return BC_STS_ERR_USAGE;
 307        }
 308
 309        sts = crystalhd_download_fw(ctx->adp, (uint8_t *)idata->add_cdata,
 310                                  idata->add_cdata_sz);
 311
 312        if (sts != BC_STS_SUCCESS) {
 313                BCMLOG_ERR("Firmware Download Failure!! - %d\n", sts);
 314        } else
 315                ctx->state |= BC_LINK_INIT;
 316
 317        return sts;
 318}
 319
 320/*
 321 * We use the FW_CMD interface to sync up playback state with application
 322 * and  firmware. This function will perform the required pre and post
 323 * processing of the Firmware commands.
 324 *
 325 * Pause -
 326 *      Disable capture after decoder pause.
 327 * Resume -
 328 *      First enable capture and issue decoder resume command.
 329 * Flush -
 330 *      Abort pending input transfers and issue decoder flush command.
 331 *
 332 */
 333static enum BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx,
 334                                        struct crystalhd_ioctl_data *idata)
 335{
 336        enum BC_STATUS sts;
 337        uint32_t *cmd;
 338
 339        if (!(ctx->state & BC_LINK_INIT)) {
 340                BCMLOG_ERR("Link invalid state %d\n", ctx->state);
 341                return BC_STS_ERR_USAGE;
 342        }
 343
 344        cmd = idata->udata.u.fwCmd.cmd;
 345
 346        /* Pre-Process */
 347        if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
 348                if (!cmd[3]) {
 349                        ctx->state &= ~BC_LINK_PAUSED;
 350                        crystalhd_hw_unpause(&ctx->hw_ctx);
 351                }
 352        } else if (cmd[0] == eCMD_C011_DEC_CHAN_FLUSH) {
 353                BCMLOG(BCMLOG_INFO, "Flush issued\n");
 354                if (cmd[3])
 355                        ctx->cin_wait_exit = 1;
 356        }
 357
 358        sts = crystalhd_do_fw_cmd(&ctx->hw_ctx, &idata->udata.u.fwCmd);
 359
 360        if (sts != BC_STS_SUCCESS) {
 361                BCMLOG(BCMLOG_INFO, "fw cmd %x failed\n", cmd[0]);
 362                return sts;
 363        }
 364
 365        /* Post-Process */
 366        if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
 367                if (cmd[3]) {
 368                        ctx->state |= BC_LINK_PAUSED;
 369                        crystalhd_hw_pause(&ctx->hw_ctx);
 370                }
 371        }
 372
 373        return sts;
 374}
 375
 376static void bc_proc_in_completion(struct crystalhd_dio_req *dio_hnd,
 377                                  wait_queue_head_t *event, enum BC_STATUS sts)
 378{
 379        if (!dio_hnd || !event) {
 380                BCMLOG_ERR("Invalid Arg!!\n");
 381                return;
 382        }
 383        if (sts == BC_STS_IO_USER_ABORT)
 384                return;
 385
 386        dio_hnd->uinfo.comp_sts = sts;
 387        dio_hnd->uinfo.ev_sts = 1;
 388        crystalhd_set_event(event);
 389}
 390
 391static enum BC_STATUS bc_cproc_codein_sleep(struct crystalhd_cmd *ctx)
 392{
 393        wait_queue_head_t sleep_ev;
 394        int rc = 0;
 395
 396        if (ctx->state & BC_LINK_SUSPEND)
 397                return BC_STS_IO_USER_ABORT;
 398
 399        if (ctx->cin_wait_exit) {
 400                ctx->cin_wait_exit = 0;
 401                return BC_STS_CMD_CANCELLED;
 402        }
 403        crystalhd_create_event(&sleep_ev);
 404        crystalhd_wait_on_event(&sleep_ev, 0, 100, rc, 0);
 405        if (rc == -EINTR)
 406                return BC_STS_IO_USER_ABORT;
 407
 408        return BC_STS_SUCCESS;
 409}
 410
 411static enum BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx,
 412                                   struct crystalhd_ioctl_data *idata,
 413                                   struct crystalhd_dio_req *dio)
 414{
 415        uint32_t tx_listid = 0;
 416        enum BC_STATUS sts = BC_STS_SUCCESS;
 417        wait_queue_head_t event;
 418        int rc = 0;
 419
 420        if (!ctx || !idata || !dio) {
 421                BCMLOG_ERR("Invalid Arg!!\n");
 422                return BC_STS_INV_ARG;
 423        }
 424
 425        crystalhd_create_event(&event);
 426
 427        ctx->tx_list_id = 0;
 428        /* msleep_interruptible(2000); */
 429        sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, bc_proc_in_completion,
 430                                 &event, &tx_listid,
 431                                 idata->udata.u.ProcInput.Encrypted);
 432
 433        while (sts == BC_STS_BUSY) {
 434                sts = bc_cproc_codein_sleep(ctx);
 435                if (sts != BC_STS_SUCCESS)
 436                        break;
 437                sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio,
 438                                         bc_proc_in_completion,
 439                                         &event, &tx_listid,
 440                                         idata->udata.u.ProcInput.Encrypted);
 441        }
 442        if (sts != BC_STS_SUCCESS) {
 443                BCMLOG(BCMLOG_DBG, "_hw_txdma returning sts:%d\n", sts);
 444                return sts;
 445        }
 446        if (ctx->cin_wait_exit)
 447                ctx->cin_wait_exit = 0;
 448
 449        ctx->tx_list_id = tx_listid;
 450
 451        /* _post() succeeded.. wait for the completion. */
 452        crystalhd_wait_on_event(&event, (dio->uinfo.ev_sts), 3000, rc, 0);
 453        ctx->tx_list_id = 0;
 454        if (!rc) {
 455                return dio->uinfo.comp_sts;
 456        } else if (rc == -EBUSY) {
 457                BCMLOG(BCMLOG_DBG, "_tx_post() T/O\n");
 458                sts = BC_STS_TIMEOUT;
 459        } else if (rc == -EINTR) {
 460                BCMLOG(BCMLOG_DBG, "Tx Wait Signal int.\n");
 461                sts = BC_STS_IO_USER_ABORT;
 462        } else {
 463                sts = BC_STS_IO_ERROR;
 464        }
 465
 466        /* We are cancelling the IO from the same context as the _post().
 467         * so no need to wait on the event again.. the return itself
 468         * ensures the release of our resources.
 469         */
 470        crystalhd_hw_cancel_tx(&ctx->hw_ctx, tx_listid);
 471
 472        return sts;
 473}
 474
 475/* Helper function to check on user buffers */
 476static enum BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz,
 477                                        uint32_t uv_off, bool en_422)
 478{
 479        if (!ubuff || !ub_sz) {
 480                BCMLOG_ERR("%s->Invalid Arg %p %x\n",
 481                        ((pin) ? "TX" : "RX"), ubuff, ub_sz);
 482                return BC_STS_INV_ARG;
 483        }
 484
 485        /* Check for alignment */
 486        if (((uintptr_t)ubuff) & 0x03) {
 487                BCMLOG_ERR("%s-->Un-aligned address not implemented yet.. %p\n",
 488                                ((pin) ? "TX" : "RX"), ubuff);
 489                return BC_STS_NOT_IMPL;
 490        }
 491        if (pin)
 492                return BC_STS_SUCCESS;
 493
 494        if (!en_422 && !uv_off) {
 495                BCMLOG_ERR("Need UV offset for 420 mode.\n");
 496                return BC_STS_INV_ARG;
 497        }
 498
 499        if (en_422 && uv_off) {
 500                BCMLOG_ERR("UV offset in 422 mode ??\n");
 501                return BC_STS_INV_ARG;
 502        }
 503
 504        return BC_STS_SUCCESS;
 505}
 506
 507static enum BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx,
 508                                        struct crystalhd_ioctl_data *idata)
 509{
 510        void *ubuff;
 511        uint32_t ub_sz;
 512        struct crystalhd_dio_req *dio_hnd = NULL;
 513        enum BC_STATUS sts = BC_STS_SUCCESS;
 514
 515        if (!ctx || !idata) {
 516                BCMLOG_ERR("Invalid Arg!!\n");
 517                return BC_STS_INV_ARG;
 518        }
 519
 520        ubuff = idata->udata.u.ProcInput.pDmaBuff;
 521        ub_sz = idata->udata.u.ProcInput.BuffSz;
 522
 523        sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0);
 524        if (sts != BC_STS_SUCCESS)
 525                return sts;
 526
 527        sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd);
 528        if (sts != BC_STS_SUCCESS) {
 529                BCMLOG_ERR("dio map - %d\n", sts);
 530                return sts;
 531        }
 532
 533        if (!dio_hnd)
 534                return BC_STS_ERROR;
 535
 536        sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd);
 537
 538        crystalhd_unmap_dio(ctx->adp, dio_hnd);
 539
 540        return sts;
 541}
 542
 543static enum BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx,
 544                                       struct crystalhd_ioctl_data *idata)
 545{
 546        void *ubuff;
 547        uint32_t ub_sz, uv_off;
 548        bool en_422;
 549        struct crystalhd_dio_req *dio_hnd = NULL;
 550        enum BC_STATUS sts = BC_STS_SUCCESS;
 551
 552        if (!ctx || !idata) {
 553                BCMLOG_ERR("Invalid Arg!!\n");
 554                return BC_STS_INV_ARG;
 555        }
 556
 557        ubuff = idata->udata.u.RxBuffs.YuvBuff;
 558        ub_sz = idata->udata.u.RxBuffs.YuvBuffSz;
 559        uv_off = idata->udata.u.RxBuffs.UVbuffOffset;
 560        en_422 = idata->udata.u.RxBuffs.b422Mode;
 561
 562        sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422);
 563        if (sts != BC_STS_SUCCESS)
 564                return sts;
 565
 566        sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off,
 567                              en_422, 0, &dio_hnd);
 568        if (sts != BC_STS_SUCCESS) {
 569                BCMLOG_ERR("dio map - %d\n", sts);
 570                return sts;
 571        }
 572
 573        if (!dio_hnd)
 574                return BC_STS_ERROR;
 575
 576        sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY));
 577        if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) {
 578                crystalhd_unmap_dio(ctx->adp, dio_hnd);
 579                return sts;
 580        }
 581
 582        return BC_STS_SUCCESS;
 583}
 584
 585static enum BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx,
 586                                     struct crystalhd_dio_req *dio)
 587{
 588        enum BC_STATUS sts = BC_STS_SUCCESS;
 589
 590        sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0);
 591        if (sts != BC_STS_SUCCESS)
 592                return sts;
 593
 594        ctx->state |= BC_LINK_FMT_CHG;
 595        if (ctx->state == BC_LINK_READY)
 596                sts = crystalhd_hw_start_capture(&ctx->hw_ctx);
 597
 598        return sts;
 599}
 600
 601static enum BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx,
 602                                      struct crystalhd_ioctl_data *idata)
 603{
 604        struct crystalhd_dio_req *dio = NULL;
 605        enum BC_STATUS sts = BC_STS_SUCCESS;
 606        struct BC_DEC_OUT_BUFF *frame;
 607
 608        if (!ctx || !idata) {
 609                BCMLOG_ERR("Invalid Arg!!\n");
 610                return BC_STS_INV_ARG;
 611        }
 612
 613        if (!(ctx->state & BC_LINK_CAP_EN)) {
 614                BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state);
 615                return BC_STS_ERR_USAGE;
 616        }
 617
 618        frame = &idata->udata.u.DecOutData;
 619
 620        sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
 621        if (sts != BC_STS_SUCCESS)
 622                return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts;
 623
 624        frame->Flags = dio->uinfo.comp_flags;
 625
 626        if (frame->Flags & COMP_FLAG_FMT_CHANGE)
 627                return bc_cproc_fmt_change(ctx, dio);
 628
 629        frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff;
 630        frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len;
 631        frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset;
 632        frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode;
 633
 634        frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz;
 635        frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz;
 636
 637        crystalhd_unmap_dio(ctx->adp, dio);
 638
 639        return BC_STS_SUCCESS;
 640}
 641
 642static enum BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx,
 643                                        struct crystalhd_ioctl_data *idata)
 644{
 645        ctx->state |= BC_LINK_CAP_EN;
 646        if (ctx->state == BC_LINK_READY)
 647                return crystalhd_hw_start_capture(&ctx->hw_ctx);
 648
 649        return BC_STS_SUCCESS;
 650}
 651
 652static enum BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx,
 653                                          struct crystalhd_ioctl_data *idata)
 654{
 655        struct crystalhd_dio_req *dio = NULL;
 656        enum BC_STATUS sts = BC_STS_SUCCESS;
 657        struct BC_DEC_OUT_BUFF *frame;
 658        uint32_t count;
 659
 660        if (!ctx || !idata) {
 661                BCMLOG_ERR("Invalid Arg!!\n");
 662                return BC_STS_INV_ARG;
 663        }
 664
 665        if (!(ctx->state & BC_LINK_CAP_EN))
 666                return BC_STS_ERR_USAGE;
 667
 668        /* We should ack flush even when we are in paused/suspend state */
 669        if (!(ctx->state & BC_LINK_READY))
 670                return crystalhd_hw_stop_capture(&ctx->hw_ctx);
 671
 672        ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG);
 673
 674        frame = &idata->udata.u.DecOutData;
 675        for (count = 0; count < BC_RX_LIST_CNT; count++) {
 676
 677                sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
 678                if (sts != BC_STS_SUCCESS)
 679                        break;
 680
 681                crystalhd_unmap_dio(ctx->adp, dio);
 682        }
 683
 684        return crystalhd_hw_stop_capture(&ctx->hw_ctx);
 685}
 686
 687static enum BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx,
 688                                    struct crystalhd_ioctl_data *idata)
 689{
 690        struct BC_DTS_STATS *stats;
 691        struct crystalhd_hw_stats       hw_stats;
 692
 693        if (!ctx || !idata) {
 694                BCMLOG_ERR("Invalid Arg!!\n");
 695                return BC_STS_INV_ARG;
 696        }
 697
 698        crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats);
 699
 700        stats = &idata->udata.u.drvStat;
 701        stats->drvRLL = hw_stats.rdyq_count;
 702        stats->drvFLL = hw_stats.freeq_count;
 703        stats->DrvTotalFrmDropped = hw_stats.rx_errors;
 704        stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors;
 705        stats->intCount = hw_stats.num_interrupts;
 706        stats->DrvIgnIntrCnt = hw_stats.num_interrupts -
 707                                hw_stats.dev_interrupts;
 708        stats->TxFifoBsyCnt = hw_stats.cin_busy;
 709        stats->pauseCount = hw_stats.pause_cnt;
 710
 711        if (ctx->pwr_state_change)
 712                stats->pwr_state_change = 1;
 713        if (ctx->state & BC_LINK_PAUSED)
 714                stats->DrvPauseTime = 1;
 715
 716        return BC_STS_SUCCESS;
 717}
 718
 719static enum BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx,
 720                                      struct crystalhd_ioctl_data *idata)
 721{
 722        crystalhd_hw_stats(&ctx->hw_ctx, NULL);
 723
 724        return BC_STS_SUCCESS;
 725}
 726
 727static enum BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx,
 728                                  struct crystalhd_ioctl_data *idata)
 729{
 730        struct BC_CLOCK *clock;
 731        uint32_t oldClk;
 732        enum BC_STATUS sts = BC_STS_SUCCESS;
 733
 734        if (!ctx || !idata) {
 735                BCMLOG_ERR("Invalid Arg!!\n");
 736                return BC_STS_INV_ARG;
 737        }
 738
 739        clock = &idata->udata.u.clockValue;
 740        oldClk = ctx->hw_ctx.core_clock_mhz;
 741        ctx->hw_ctx.core_clock_mhz = clock->clk;
 742
 743        if (ctx->state & BC_LINK_READY) {
 744                sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx);
 745                if (sts == BC_STS_CLK_NOCHG)
 746                        ctx->hw_ctx.core_clock_mhz = oldClk;
 747        }
 748
 749        clock->clk = ctx->hw_ctx.core_clock_mhz;
 750
 751        return sts;
 752}
 753
 754/*=============== Cmd Proc Table.. ======================================*/
 755static const struct crystalhd_cmd_tbl   g_crystalhd_cproc_tbl[] = {
 756        { BCM_IOC_GET_VERSION,          bc_cproc_get_version,   0},
 757        { BCM_IOC_GET_HWTYPE,           bc_cproc_get_hwtype,    0},
 758        { BCM_IOC_REG_RD,               bc_cproc_reg_rd,        0},
 759        { BCM_IOC_REG_WR,               bc_cproc_reg_wr,        0},
 760        { BCM_IOC_FPGA_RD,              bc_cproc_link_reg_rd,   0},
 761        { BCM_IOC_FPGA_WR,              bc_cproc_link_reg_wr,   0},
 762        { BCM_IOC_MEM_RD,               bc_cproc_mem_rd,        0},
 763        { BCM_IOC_MEM_WR,               bc_cproc_mem_wr,        0},
 764        { BCM_IOC_RD_PCI_CFG,           bc_cproc_cfg_rd,        0},
 765        { BCM_IOC_WR_PCI_CFG,           bc_cproc_cfg_wr,        1},
 766        { BCM_IOC_FW_DOWNLOAD,          bc_cproc_download_fw,   1},
 767        { BCM_IOC_FW_CMD,               bc_cproc_do_fw_cmd,     1},
 768        { BCM_IOC_PROC_INPUT,           bc_cproc_proc_input,    1},
 769        { BCM_IOC_ADD_RXBUFFS,          bc_cproc_add_cap_buff,  1},
 770        { BCM_IOC_FETCH_RXBUFF,         bc_cproc_fetch_frame,   1},
 771        { BCM_IOC_START_RX_CAP,         bc_cproc_start_capture, 1},
 772        { BCM_IOC_FLUSH_RX_CAP,         bc_cproc_flush_cap_buffs, 1},
 773        { BCM_IOC_GET_DRV_STAT,         bc_cproc_get_stats,     0},
 774        { BCM_IOC_RST_DRV_STAT,         bc_cproc_reset_stats,   0},
 775        { BCM_IOC_NOTIFY_MODE,          bc_cproc_notify_mode,   0},
 776        { BCM_IOC_CHG_CLK,              bc_cproc_chg_clk, 0},
 777        { BCM_IOC_END,                  NULL},
 778};
 779
 780/*=============== Cmd Proc Functions.. ===================================*/
 781
 782/**
 783 * crystalhd_suspend - Power management suspend request.
 784 * @ctx: Command layer context.
 785 * @idata: Iodata - required for internal use.
 786 *
 787 * Return:
 788 *      status
 789 *
 790 * 1. Set the state to Suspend.
 791 * 2. Flush the Rx Buffers it will unmap all the buffers and
 792 *    stop the RxDMA engine.
 793 * 3. Cancel The TX Io and Stop Dma Engine.
 794 * 4. Put the DDR in to deep sleep.
 795 * 5. Stop the hardware putting it in to Reset State.
 796 *
 797 * Current gstreamer frame work does not provide any power management
 798 * related notification to user mode decoder plug-in. As a work-around
 799 * we pass on the power mangement notification to our plug-in by completing
 800 * all outstanding requests with BC_STS_IO_USER_ABORT return code.
 801 */
 802enum BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx,
 803                                struct crystalhd_ioctl_data *idata)
 804{
 805        enum BC_STATUS sts = BC_STS_SUCCESS;
 806
 807        if (!ctx || !idata) {
 808                BCMLOG_ERR("Invalid Parameters\n");
 809                return BC_STS_ERROR;
 810        }
 811
 812        if (ctx->state & BC_LINK_SUSPEND)
 813                return BC_STS_SUCCESS;
 814
 815        if (ctx->state == BC_LINK_INVALID) {
 816                BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
 817                return BC_STS_SUCCESS;
 818        }
 819
 820        ctx->state |= BC_LINK_SUSPEND;
 821
 822        bc_cproc_mark_pwr_state(ctx);
 823
 824        if (ctx->state & BC_LINK_CAP_EN) {
 825                sts = bc_cproc_flush_cap_buffs(ctx, idata);
 826                if (sts != BC_STS_SUCCESS)
 827                        return sts;
 828        }
 829
 830        if (ctx->tx_list_id) {
 831                sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
 832                if (sts != BC_STS_SUCCESS)
 833                        return sts;
 834        }
 835
 836        sts = crystalhd_hw_suspend(&ctx->hw_ctx);
 837        if (sts != BC_STS_SUCCESS)
 838                return sts;
 839
 840        BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
 841
 842        return BC_STS_SUCCESS;
 843}
 844
 845/**
 846 * crystalhd_resume - Resume frame capture.
 847 * @ctx: Command layer contextx.
 848 *
 849 * Return:
 850 *      status
 851 *
 852 *
 853 * Resume frame capture.
 854 *
 855 * PM_Resume can't resume the playback state back to pre-suspend state
 856 * because we don't keep video clip related information within driver.
 857 * To get back to the pre-suspend state App will re-open the device and
 858 * start a new playback session from the pre-suspend clip position.
 859 *
 860 */
 861enum BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
 862{
 863        BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
 864
 865        bc_cproc_mark_pwr_state(ctx);
 866
 867        return BC_STS_SUCCESS;
 868}
 869
 870/**
 871 * crystalhd_user_open - Create application handle.
 872 * @ctx: Command layer contextx.
 873 * @user_ctx: User ID context.
 874 *
 875 * Return:
 876 *      status
 877 *
 878 * Creates an application specific UID and allocates
 879 * application specific resources. HW layer initialization
 880 * is done for the first open request.
 881 */
 882enum BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
 883                            struct crystalhd_user **user_ctx)
 884{
 885        struct crystalhd_user *uc;
 886
 887        if (!ctx || !user_ctx) {
 888                BCMLOG_ERR("Invalid arg..\n");
 889                return BC_STS_INV_ARG;
 890        }
 891
 892        uc = bc_cproc_get_uid(ctx);
 893        if (!uc) {
 894                BCMLOG(BCMLOG_INFO, "No free user context...\n");
 895                return BC_STS_BUSY;
 896        }
 897
 898        BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
 899
 900        crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
 901
 902        uc->in_use = 1;
 903
 904        *user_ctx = uc;
 905
 906        return BC_STS_SUCCESS;
 907}
 908
 909/**
 910 * crystalhd_user_close - Close application handle.
 911 * @ctx: Command layer contextx.
 912 * @uc: User ID context.
 913 *
 914 * Return:
 915 *      status
 916 *
 917 * Closer application handle and release app specific
 918 * resources.
 919 */
 920enum BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
 921{
 922        uint32_t mode = uc->mode;
 923
 924        ctx->user[uc->uid].mode = DTS_MODE_INV;
 925        ctx->user[uc->uid].in_use = 0;
 926        ctx->cin_wait_exit = 1;
 927        ctx->pwr_state_change = 0;
 928
 929        BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
 930
 931        if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
 932                crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
 933                crystalhd_destroy_dio_pool(ctx->adp);
 934        } else if (bc_cproc_get_user_count(ctx)) {
 935                return BC_STS_SUCCESS;
 936        }
 937
 938        crystalhd_hw_close(&ctx->hw_ctx);
 939
 940        ctx->state = BC_LINK_INVALID;
 941
 942        return BC_STS_SUCCESS;
 943}
 944
 945/**
 946 * crystalhd_setup_cmd_context - Setup Command layer resources.
 947 * @ctx: Command layer contextx.
 948 * @adp: Adapter context
 949 *
 950 * Return:
 951 *      status
 952 *
 953 * Called at the time of driver load.
 954 */
 955enum BC_STATUS __devinit crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
 956                                    struct crystalhd_adp *adp)
 957{
 958        int i = 0;
 959
 960        if (!ctx || !adp) {
 961                BCMLOG_ERR("Invalid arg!!\n");
 962                return BC_STS_INV_ARG;
 963        }
 964
 965        if (ctx->adp)
 966                BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
 967
 968        ctx->adp = adp;
 969        for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
 970                ctx->user[i].uid = i;
 971                ctx->user[i].in_use = 0;
 972                ctx->user[i].mode = DTS_MODE_INV;
 973        }
 974
 975        /*Open and Close the Hardware to put it in to sleep state*/
 976        crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
 977        crystalhd_hw_close(&ctx->hw_ctx);
 978        return BC_STS_SUCCESS;
 979}
 980
 981/**
 982 * crystalhd_delete_cmd_context - Release Command layer resources.
 983 * @ctx: Command layer contextx.
 984 *
 985 * Return:
 986 *      status
 987 *
 988 * Called at the time of driver un-load.
 989 */
 990enum BC_STATUS __devexit crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
 991{
 992        BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
 993
 994        ctx->adp = NULL;
 995
 996        return BC_STS_SUCCESS;
 997}
 998
 999/**
1000 * crystalhd_get_cmd_proc  - Cproc table lookup.
1001 * @ctx: Command layer contextx.
1002 * @cmd: IOCTL command code.
1003 * @uc: User ID context.
1004 *
1005 * Return:
1006 *      command proc function pointer
1007 *
1008 * This function checks the process context, application's
1009 * mode of operation and returns the function pointer
1010 * from the cproc table.
1011 */
1012crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
1013                                      struct crystalhd_user *uc)
1014{
1015        crystalhd_cmd_proc cproc = NULL;
1016        unsigned int i, tbl_sz;
1017
1018        if (!ctx) {
1019                BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
1020                return NULL;
1021        }
1022
1023        if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
1024                BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
1025                return NULL;
1026        }
1027
1028        tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(struct crystalhd_cmd_tbl);
1029        for (i = 0; i < tbl_sz; i++) {
1030                if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
1031                        if ((uc->mode == DTS_MONITOR_MODE) &&
1032                            (g_crystalhd_cproc_tbl[i].block_mon)) {
1033                                BCMLOG(BCMLOG_INFO, "Blocking cmd %d\n", cmd);
1034                                break;
1035                        }
1036                        cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
1037                        break;
1038                }
1039        }
1040
1041        return cproc;
1042}
1043
1044/**
1045 * crystalhd_cmd_interrupt - ISR entry point
1046 * @ctx: Command layer contextx.
1047 *
1048 * Return:
1049 *      TRUE: If interrupt from bcm70012 device.
1050 *
1051 *
1052 * ISR entry point from OS layer.
1053 */
1054bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
1055{
1056        if (!ctx) {
1057                BCMLOG_ERR("Invalid arg..\n");
1058                return 0;
1059        }
1060
1061        return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
1062}
1063