Merge git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-2.6-fixes
[pandora-kernel.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_cmds.h"
28 #include "crystalhd_hw.h"
29
30 static 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
45 static 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
57 static 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
72 static 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
114 static 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
129 static 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
147 static 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
157 static 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
169 static 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
180 static 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
192 static 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
211 static 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
230 static 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
262 static 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
294 static 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  */
333 static 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
376 static 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
391 static 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
411 static 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 */
476 static 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
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, (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) ? 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
642 static 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
652 static 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
687 static 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
719 static 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
727 static 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.. ======================================*/
755 static 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  */
802 enum 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  */
861 enum 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  */
882 enum 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 aplication handle and release app specific
918  * resources.
919  */
920 enum 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  */
955 enum 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  */
990 enum 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  */
1012 crystalhd_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  */
1054 bool 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 }