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