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