staging: add Broadcom Crystal HD driver
[safe/jmp/linux-2.6] / 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 BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx,
73                                       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 BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx,
115                                       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 BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx, 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 BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx,
147                                  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 BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx,
157                                  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 BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx,
169                                       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 BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx,
180                                       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 BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx,
192                                  crystalhd_ioctl_data *idata)
193 {
194         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 BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx,
211                                  crystalhd_ioctl_data *idata)
212 {
213         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 BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx,
230                                  crystalhd_ioctl_data *idata)
231 {
232         uint32_t ix, cnt, off, len;
233         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 BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx,
262                                  crystalhd_ioctl_data *idata)
263 {
264         uint32_t ix, cnt, off, len;
265         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 BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx,
294                                       crystalhd_ioctl_data *idata)
295 {
296         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 BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
333 {
334         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(crystalhd_dio_req *dio_hnd,
375                                   wait_queue_head_t *event, 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 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 BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx,
410                                    crystalhd_ioctl_data *idata,
411                                    crystalhd_dio_req *dio)
412 {
413         uint32_t tx_listid = 0;
414         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 BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz,
475                                         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("%s-->Un-aligned address not implemented yet.. %p \n",
486                                 ((pin) ? "TX" : "RX"), ubuff);
487                 return BC_STS_NOT_IMPL;
488         }
489         if (pin)
490                 return BC_STS_SUCCESS;
491
492         if (!en_422 && !uv_off) {
493                 BCMLOG_ERR("Need UV offset for 420 mode.\n");
494                 return BC_STS_INV_ARG;
495         }
496
497         if (en_422 && uv_off) {
498                 BCMLOG_ERR("UV offset in 422 mode ??\n");
499                 return BC_STS_INV_ARG;
500         }
501
502         return BC_STS_SUCCESS;
503 }
504
505 static BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
506 {
507         void *ubuff;
508         uint32_t ub_sz;
509         crystalhd_dio_req *dio_hnd = NULL;
510         BC_STATUS sts = BC_STS_SUCCESS;
511
512         if (!ctx || !idata) {
513                 BCMLOG_ERR("Invalid Arg!!\n");
514                 return BC_STS_INV_ARG;
515         }
516
517         ubuff = idata->udata.u.ProcInput.pDmaBuff;
518         ub_sz = idata->udata.u.ProcInput.BuffSz;
519
520         sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0);
521         if (sts != BC_STS_SUCCESS)
522                 return sts;
523
524         sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd);
525         if (sts != BC_STS_SUCCESS) {
526                 BCMLOG_ERR("dio map - %d \n", sts);
527                 return sts;
528         }
529
530         if (!dio_hnd)
531                 return BC_STS_ERROR;
532
533         sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd);
534
535         crystalhd_unmap_dio(ctx->adp, dio_hnd);
536
537         return sts;
538 }
539
540 static BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx,
541                                        crystalhd_ioctl_data *idata)
542 {
543         void *ubuff;
544         uint32_t ub_sz, uv_off;
545         bool en_422;
546         crystalhd_dio_req *dio_hnd = NULL;
547         BC_STATUS sts = BC_STS_SUCCESS;
548
549         if (!ctx || !idata) {
550                 BCMLOG_ERR("Invalid Arg!!\n");
551                 return BC_STS_INV_ARG;
552         }
553
554         ubuff = idata->udata.u.RxBuffs.YuvBuff;
555         ub_sz = idata->udata.u.RxBuffs.YuvBuffSz;
556         uv_off = idata->udata.u.RxBuffs.UVbuffOffset;
557         en_422 = idata->udata.u.RxBuffs.b422Mode;
558
559         sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422);
560         if (sts != BC_STS_SUCCESS)
561                 return sts;
562
563         sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off,
564                               en_422, 0, &dio_hnd);
565         if (sts != BC_STS_SUCCESS) {
566                 BCMLOG_ERR("dio map - %d \n", sts);
567                 return sts;
568         }
569
570         if (!dio_hnd)
571                 return BC_STS_ERROR;
572
573         sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY));
574         if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) {
575                 crystalhd_unmap_dio(ctx->adp, dio_hnd);
576                 return sts;
577         }
578
579         return BC_STS_SUCCESS;
580 }
581
582 static BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx,
583                                      crystalhd_dio_req *dio)
584 {
585         BC_STATUS sts = BC_STS_SUCCESS;
586
587         sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0);
588         if (sts != BC_STS_SUCCESS)
589                 return sts;
590
591         ctx->state |= BC_LINK_FMT_CHG;
592         if (ctx->state == BC_LINK_READY)
593                 sts = crystalhd_hw_start_capture(&ctx->hw_ctx);
594
595         return sts;
596 }
597
598 static BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx,
599                                       crystalhd_ioctl_data *idata)
600 {
601         crystalhd_dio_req *dio = NULL;
602         BC_STATUS sts = BC_STS_SUCCESS;
603         BC_DEC_OUT_BUFF *frame;
604
605         if (!ctx || !idata) {
606                 BCMLOG_ERR("Invalid Arg!!\n");
607                 return BC_STS_INV_ARG;
608         }
609
610         if (!(ctx->state & BC_LINK_CAP_EN)) {
611                 BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state);
612                 return BC_STS_ERR_USAGE;
613         }
614
615         frame = &idata->udata.u.DecOutData;
616
617         sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
618         if (sts != BC_STS_SUCCESS)
619                 return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts;
620
621         frame->Flags = dio->uinfo.comp_flags;
622
623         if (frame->Flags & COMP_FLAG_FMT_CHANGE)
624                 return bc_cproc_fmt_change(ctx, dio);
625
626         frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff;
627         frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len;
628         frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset;
629         frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode;
630
631         frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz;
632         frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz;
633
634         crystalhd_unmap_dio(ctx->adp, dio);
635
636         return BC_STS_SUCCESS;
637 }
638
639 static BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx,
640                                         crystalhd_ioctl_data *idata)
641 {
642         ctx->state |= BC_LINK_CAP_EN;
643         if (ctx->state == BC_LINK_READY)
644                 return crystalhd_hw_start_capture(&ctx->hw_ctx);
645
646         return BC_STS_SUCCESS;
647 }
648
649 static BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx,
650                                           crystalhd_ioctl_data *idata)
651 {
652         crystalhd_dio_req *dio = NULL;
653         BC_STATUS sts = BC_STS_SUCCESS;
654         BC_DEC_OUT_BUFF *frame;
655         uint32_t count;
656
657         if (!ctx || !idata) {
658                 BCMLOG_ERR("Invalid Arg!!\n");
659                 return BC_STS_INV_ARG;
660         }
661
662         if (!(ctx->state & BC_LINK_CAP_EN))
663                 return BC_STS_ERR_USAGE;
664
665         /* We should ack flush even when we are in paused/suspend state */
666         if (!(ctx->state & BC_LINK_READY))
667                 return crystalhd_hw_stop_capture(&ctx->hw_ctx);
668
669         ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG);
670
671         frame = &idata->udata.u.DecOutData;
672         for (count = 0; count < BC_RX_LIST_CNT; count++) {
673
674                 sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
675                 if (sts != BC_STS_SUCCESS)
676                         break;
677
678                 crystalhd_unmap_dio(ctx->adp, dio);
679         }
680
681         return crystalhd_hw_stop_capture(&ctx->hw_ctx);
682 }
683
684 static BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx,
685                                     crystalhd_ioctl_data *idata)
686 {
687         BC_DTS_STATS *stats;
688         struct crystalhd_hw_stats       hw_stats;
689
690         if (!ctx || !idata) {
691                 BCMLOG_ERR("Invalid Arg!!\n");
692                 return BC_STS_INV_ARG;
693         }
694
695         crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats);
696
697         stats = &idata->udata.u.drvStat;
698         stats->drvRLL = hw_stats.rdyq_count;
699         stats->drvFLL = hw_stats.freeq_count;
700         stats->DrvTotalFrmDropped = hw_stats.rx_errors;
701         stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors;
702         stats->intCount = hw_stats.num_interrupts;
703         stats->DrvIgnIntrCnt = hw_stats.num_interrupts -
704                                 hw_stats.dev_interrupts;
705         stats->TxFifoBsyCnt = hw_stats.cin_busy;
706         stats->pauseCount = hw_stats.pause_cnt;
707
708         if (ctx->pwr_state_change)
709                 stats->pwr_state_change = 1;
710         if (ctx->state & BC_LINK_PAUSED)
711                 stats->DrvPauseTime = 1;
712
713         return BC_STS_SUCCESS;
714 }
715
716 static BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx,
717                                       crystalhd_ioctl_data *idata)
718 {
719         crystalhd_hw_stats(&ctx->hw_ctx, NULL);
720
721         return BC_STS_SUCCESS;
722 }
723
724 static BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx,
725                                   crystalhd_ioctl_data *idata)
726 {
727         BC_CLOCK *clock;
728         uint32_t oldClk;
729         BC_STATUS sts = BC_STS_SUCCESS;
730
731         if (!ctx || !idata) {
732                 BCMLOG_ERR("Invalid Arg!!\n");
733                 return BC_STS_INV_ARG;
734         }
735
736         clock = &idata->udata.u.clockValue;
737         oldClk = ctx->hw_ctx.core_clock_mhz;
738         ctx->hw_ctx.core_clock_mhz = clock->clk;
739
740         if (ctx->state & BC_LINK_READY) {
741                 sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx);
742                 if (sts == BC_STS_CLK_NOCHG)
743                         ctx->hw_ctx.core_clock_mhz = oldClk;
744         }
745
746         clock->clk = ctx->hw_ctx.core_clock_mhz;
747
748         return sts;
749 }
750
751 /*=============== Cmd Proc Table.. ======================================*/
752 static const crystalhd_cmd_tbl_t        g_crystalhd_cproc_tbl[] = {
753         { BCM_IOC_GET_VERSION,          bc_cproc_get_version,   0},
754         { BCM_IOC_GET_HWTYPE,           bc_cproc_get_hwtype,    0},
755         { BCM_IOC_REG_RD,               bc_cproc_reg_rd,        0},
756         { BCM_IOC_REG_WR,               bc_cproc_reg_wr,        0},
757         { BCM_IOC_FPGA_RD,              bc_cproc_link_reg_rd,   0},
758         { BCM_IOC_FPGA_WR,              bc_cproc_link_reg_wr,   0},
759         { BCM_IOC_MEM_RD,               bc_cproc_mem_rd,        0},
760         { BCM_IOC_MEM_WR,               bc_cproc_mem_wr,        0},
761         { BCM_IOC_RD_PCI_CFG,           bc_cproc_cfg_rd,        0},
762         { BCM_IOC_WR_PCI_CFG,           bc_cproc_cfg_wr,        1},
763         { BCM_IOC_FW_DOWNLOAD,          bc_cproc_download_fw,   1},
764         { BCM_IOC_FW_CMD,               bc_cproc_do_fw_cmd,     1},
765         { BCM_IOC_PROC_INPUT,           bc_cproc_proc_input,    1},
766         { BCM_IOC_ADD_RXBUFFS,          bc_cproc_add_cap_buff,  1},
767         { BCM_IOC_FETCH_RXBUFF,         bc_cproc_fetch_frame,   1},
768         { BCM_IOC_START_RX_CAP,         bc_cproc_start_capture, 1},
769         { BCM_IOC_FLUSH_RX_CAP,         bc_cproc_flush_cap_buffs, 1},
770         { BCM_IOC_GET_DRV_STAT,         bc_cproc_get_stats,     0},
771         { BCM_IOC_RST_DRV_STAT,         bc_cproc_reset_stats,   0},
772         { BCM_IOC_NOTIFY_MODE,          bc_cproc_notify_mode,   0},
773         { BCM_IOC_CHG_CLK,              bc_cproc_chg_clk, 0},
774         { BCM_IOC_END,                  NULL},
775 };
776
777 /*=============== Cmd Proc Functions.. ===================================*/
778
779 /**
780  * crystalhd_suspend - Power management suspend request.
781  * @ctx: Command layer context.
782  * @idata: Iodata - required for internal use.
783  *
784  * Return:
785  *      status
786  *
787  * 1. Set the state to Suspend.
788  * 2. Flush the Rx Buffers it will unmap all the buffers and
789  *    stop the RxDMA engine.
790  * 3. Cancel The TX Io and Stop Dma Engine.
791  * 4. Put the DDR in to deep sleep.
792  * 5. Stop the hardware putting it in to Reset State.
793  *
794  * Current gstreamer frame work does not provide any power management
795  * related notification to user mode decoder plug-in. As a work-around
796  * we pass on the power mangement notification to our plug-in by completing
797  * all outstanding requests with BC_STS_IO_USER_ABORT return code.
798  */
799 BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
800 {
801         BC_STATUS sts = BC_STS_SUCCESS;
802
803         if (!ctx || !idata) {
804                 BCMLOG_ERR("Invalid Parameters\n");
805                 return BC_STS_ERROR;
806         }
807
808         if (ctx->state & BC_LINK_SUSPEND)
809                 return BC_STS_SUCCESS;
810
811         if (ctx->state == BC_LINK_INVALID) {
812                 BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
813                 return BC_STS_SUCCESS;
814         }
815
816         ctx->state |= BC_LINK_SUSPEND;
817
818         bc_cproc_mark_pwr_state(ctx);
819
820         if (ctx->state & BC_LINK_CAP_EN) {
821                 sts = bc_cproc_flush_cap_buffs(ctx, idata);
822                 if (sts != BC_STS_SUCCESS)
823                         return sts;
824         }
825
826         if (ctx->tx_list_id) {
827                 sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
828                 if (sts != BC_STS_SUCCESS)
829                         return sts;
830         }
831
832         sts = crystalhd_hw_suspend(&ctx->hw_ctx);
833         if (sts != BC_STS_SUCCESS)
834                 return sts;
835
836         BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
837
838         return BC_STS_SUCCESS;
839 }
840
841 /**
842  * crystalhd_resume - Resume frame capture.
843  * @ctx: Command layer contextx.
844  *
845  * Return:
846  *      status
847  *
848  *
849  * Resume frame capture.
850  *
851  * PM_Resume can't resume the playback state back to pre-suspend state
852  * because we don't keep video clip related information within driver.
853  * To get back to the pre-suspend state App will re-open the device and
854  * start a new playback session from the pre-suspend clip position.
855  *
856  */
857 BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
858 {
859         BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
860
861         bc_cproc_mark_pwr_state(ctx);
862
863         return BC_STS_SUCCESS;
864 }
865
866 /**
867  * crystalhd_user_open - Create application handle.
868  * @ctx: Command layer contextx.
869  * @user_ctx: User ID context.
870  *
871  * Return:
872  *      status
873  *
874  * Creates an application specific UID and allocates
875  * application specific resources. HW layer initialization
876  * is done for the first open request.
877  */
878 BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
879                             struct crystalhd_user **user_ctx)
880 {
881         struct crystalhd_user *uc;
882
883         if (!ctx || !user_ctx) {
884                 BCMLOG_ERR("Invalid arg..\n");
885                 return BC_STS_INV_ARG;
886         }
887
888         uc = bc_cproc_get_uid(ctx);
889         if (!uc) {
890                 BCMLOG(BCMLOG_INFO, "No free user context...\n");
891                 return BC_STS_BUSY;
892         }
893
894         BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
895
896         crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
897
898         uc->in_use = 1;
899
900         *user_ctx = uc;
901
902         return BC_STS_SUCCESS;
903 }
904
905 /**
906  * crystalhd_user_close - Close application handle.
907  * @ctx: Command layer contextx.
908  * @uc: User ID context.
909  *
910  * Return:
911  *      status
912  *
913  * Closer aplication handle and release app specific
914  * resources.
915  */
916 BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
917 {
918         uint32_t mode = uc->mode;
919
920         ctx->user[uc->uid].mode = DTS_MODE_INV;
921         ctx->user[uc->uid].in_use = 0;
922         ctx->cin_wait_exit = 1;
923         ctx->pwr_state_change = 0;
924
925         BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
926
927         if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
928                 crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
929                 crystalhd_destroy_dio_pool(ctx->adp);
930         } else if (bc_cproc_get_user_count(ctx)) {
931                 return BC_STS_SUCCESS;
932         }
933
934         crystalhd_hw_close(&ctx->hw_ctx);
935
936         ctx->state = BC_LINK_INVALID;
937
938         return BC_STS_SUCCESS;
939 }
940
941 /**
942  * crystalhd_setup_cmd_context - Setup Command layer resources.
943  * @ctx: Command layer contextx.
944  * @adp: Adapter context
945  *
946  * Return:
947  *      status
948  *
949  * Called at the time of driver load.
950  */
951 BC_STATUS crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
952                                     struct crystalhd_adp *adp)
953 {
954         int i = 0;
955
956         if (!ctx || !adp) {
957                 BCMLOG_ERR("Invalid arg!!\n");
958                 return BC_STS_INV_ARG;
959         }
960
961         if (ctx->adp)
962                 BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
963
964         ctx->adp = adp;
965         for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
966                 ctx->user[i].uid = i;
967                 ctx->user[i].in_use = 0;
968                 ctx->user[i].mode = DTS_MODE_INV;
969         }
970
971         /*Open and Close the Hardware to put it in to sleep state*/
972         crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
973         crystalhd_hw_close(&ctx->hw_ctx);
974         return BC_STS_SUCCESS;
975 }
976
977 /**
978  * crystalhd_delete_cmd_context - Release Command layer resources.
979  * @ctx: Command layer contextx.
980  *
981  * Return:
982  *      status
983  *
984  * Called at the time of driver un-load.
985  */
986 BC_STATUS crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
987 {
988         BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
989
990         ctx->adp = NULL;
991
992         return BC_STS_SUCCESS;
993 }
994
995 /**
996  * crystalhd_get_cmd_proc  - Cproc table lookup.
997  * @ctx: Command layer contextx.
998  * @cmd: IOCTL command code.
999  * @uc: User ID context.
1000  *
1001  * Return:
1002  *      command proc function pointer
1003  *
1004  * This function checks the process context, application's
1005  * mode of operation and returns the function pointer
1006  * from the cproc table.
1007  */
1008 crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
1009                                       struct crystalhd_user *uc)
1010 {
1011         crystalhd_cmd_proc cproc = NULL;
1012         unsigned int i, tbl_sz;
1013
1014         if (!ctx) {
1015                 BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
1016                 return NULL;
1017         }
1018
1019         if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
1020                 BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
1021                 return NULL;
1022         }
1023
1024         tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(crystalhd_cmd_tbl_t);
1025         for (i = 0; i < tbl_sz; i++) {
1026                 if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
1027                         if ((uc->mode == DTS_MONITOR_MODE) &&
1028                             (g_crystalhd_cproc_tbl[i].block_mon)) {
1029                                 BCMLOG(BCMLOG_INFO, "Blocking cmd %d \n", cmd);
1030                                 break;
1031                         }
1032                         cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
1033                         break;
1034                 }
1035         }
1036
1037         return cproc;
1038 }
1039
1040 /**
1041  * crystalhd_cmd_interrupt - ISR entry point
1042  * @ctx: Command layer contextx.
1043  *
1044  * Return:
1045  *      TRUE: If interrupt from bcm70012 device.
1046  *
1047  *
1048  * ISR entry point from OS layer.
1049  */
1050 bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
1051 {
1052         if (!ctx) {
1053                 BCMLOG_ERR("Invalid arg..\n");
1054                 return 0;
1055         }
1056
1057         return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
1058 }