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