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
bc_cproc_get_uid(struct crystalhd_cmd * ctx)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
bc_cproc_get_user_count(struct crystalhd_cmd * ctx)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
bc_cproc_mark_pwr_state(struct crystalhd_cmd * ctx)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
bc_cproc_notify_mode(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_get_version(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_get_hwtype(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_reg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_reg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_link_reg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_link_reg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_mem_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_mem_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_cfg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_cfg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_download_fw(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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 */
bc_cproc_do_fw_cmd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_proc_in_completion(struct crystalhd_dio_req * dio_hnd,wait_queue_head_t * event,enum BC_STATUS sts)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
bc_cproc_codein_sleep(struct crystalhd_cmd * ctx)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
bc_cproc_hw_txdma(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata,struct crystalhd_dio_req * dio)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 */
bc_cproc_check_inbuffs(bool pin,void * ubuff,uint32_t ub_sz,uint32_t uv_off,bool en_422)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
bc_cproc_proc_input(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_add_cap_buff(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_fmt_change(struct crystalhd_cmd * ctx,struct crystalhd_dio_req * dio)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
bc_cproc_fetch_frame(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_start_capture(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_flush_cap_buffs(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_get_stats(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_reset_stats(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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
bc_cproc_chg_clk(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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 */
crystalhd_suspend(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)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 */
crystalhd_resume(struct crystalhd_cmd * ctx)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 */
crystalhd_user_open(struct crystalhd_cmd * ctx,struct crystalhd_user ** user_ctx)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 application handle and release app specific
918 * resources.
919 */
crystalhd_user_close(struct crystalhd_cmd * ctx,struct crystalhd_user * uc)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 */
crystalhd_setup_cmd_context(struct crystalhd_cmd * ctx,struct crystalhd_adp * adp)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 */
crystalhd_delete_cmd_context(struct crystalhd_cmd * ctx)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 */
crystalhd_get_cmd_proc(struct crystalhd_cmd * ctx,uint32_t cmd,struct crystalhd_user * uc)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 */
crystalhd_cmd_interrupt(struct crystalhd_cmd * ctx)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 }
1063