• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/staging/crystalhd/
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
30static 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
45static 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
57static 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
72static 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
114static 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
129static 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
147static 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
157static 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
169static 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
180static 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
192static 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
211static 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
230static 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
262static 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
294static 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 */
333static 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
376static 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
391static 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
411static 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 */
476static 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
507static 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
543static 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
585static 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
601static 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
642static 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
652static 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
687static 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
719static 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
727static 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.. ======================================*/
755static 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
782enum BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx,
783				struct crystalhd_ioctl_data *idata)
784{
785	enum BC_STATUS sts = BC_STS_SUCCESS;
786
787	if (!ctx || !idata) {
788		BCMLOG_ERR("Invalid Parameters\n");
789		return BC_STS_ERROR;
790	}
791
792	if (ctx->state & BC_LINK_SUSPEND)
793		return BC_STS_SUCCESS;
794
795	if (ctx->state == BC_LINK_INVALID) {
796		BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
797		return BC_STS_SUCCESS;
798	}
799
800	ctx->state |= BC_LINK_SUSPEND;
801
802	bc_cproc_mark_pwr_state(ctx);
803
804	if (ctx->state & BC_LINK_CAP_EN) {
805		sts = bc_cproc_flush_cap_buffs(ctx, idata);
806		if (sts != BC_STS_SUCCESS)
807			return sts;
808	}
809
810	if (ctx->tx_list_id) {
811		sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
812		if (sts != BC_STS_SUCCESS)
813			return sts;
814	}
815
816	sts = crystalhd_hw_suspend(&ctx->hw_ctx);
817	if (sts != BC_STS_SUCCESS)
818		return sts;
819
820	BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
821
822	return BC_STS_SUCCESS;
823}
824
825/**
826 * crystalhd_resume - Resume frame capture.
827 * @ctx: Command layer contextx.
828 *
829 * Return:
830 *	status
831 *
832 *
833 * Resume frame capture.
834 *
835 * PM_Resume can't resume the playback state back to pre-suspend state
836 * because we don't keep video clip related information within driver.
837 * To get back to the pre-suspend state App will re-open the device and
838 * start a new playback session from the pre-suspend clip position.
839 *
840 */
841enum BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
842{
843	BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
844
845	bc_cproc_mark_pwr_state(ctx);
846
847	return BC_STS_SUCCESS;
848}
849
850/**
851 * crystalhd_user_open - Create application handle.
852 * @ctx: Command layer contextx.
853 * @user_ctx: User ID context.
854 *
855 * Return:
856 *	status
857 *
858 * Creates an application specific UID and allocates
859 * application specific resources. HW layer initialization
860 * is done for the first open request.
861 */
862enum BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
863			    struct crystalhd_user **user_ctx)
864{
865	struct crystalhd_user *uc;
866
867	if (!ctx || !user_ctx) {
868		BCMLOG_ERR("Invalid arg..\n");
869		return BC_STS_INV_ARG;
870	}
871
872	uc = bc_cproc_get_uid(ctx);
873	if (!uc) {
874		BCMLOG(BCMLOG_INFO, "No free user context...\n");
875		return BC_STS_BUSY;
876	}
877
878	BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
879
880	crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
881
882	uc->in_use = 1;
883
884	*user_ctx = uc;
885
886	return BC_STS_SUCCESS;
887}
888
889/**
890 * crystalhd_user_close - Close application handle.
891 * @ctx: Command layer contextx.
892 * @uc: User ID context.
893 *
894 * Return:
895 *	status
896 *
897 * Closer aplication handle and release app specific
898 * resources.
899 */
900enum BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
901{
902	uint32_t mode = uc->mode;
903
904	ctx->user[uc->uid].mode = DTS_MODE_INV;
905	ctx->user[uc->uid].in_use = 0;
906	ctx->cin_wait_exit = 1;
907	ctx->pwr_state_change = 0;
908
909	BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
910
911	if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
912		crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
913		crystalhd_destroy_dio_pool(ctx->adp);
914	} else if (bc_cproc_get_user_count(ctx)) {
915		return BC_STS_SUCCESS;
916	}
917
918	crystalhd_hw_close(&ctx->hw_ctx);
919
920	ctx->state = BC_LINK_INVALID;
921
922	return BC_STS_SUCCESS;
923}
924
925/**
926 * crystalhd_setup_cmd_context - Setup Command layer resources.
927 * @ctx: Command layer contextx.
928 * @adp: Adapter context
929 *
930 * Return:
931 *	status
932 *
933 * Called at the time of driver load.
934 */
935enum BC_STATUS __devinit crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
936				    struct crystalhd_adp *adp)
937{
938	int i = 0;
939
940	if (!ctx || !adp) {
941		BCMLOG_ERR("Invalid arg!!\n");
942		return BC_STS_INV_ARG;
943	}
944
945	if (ctx->adp)
946		BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
947
948	ctx->adp = adp;
949	for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
950		ctx->user[i].uid = i;
951		ctx->user[i].in_use = 0;
952		ctx->user[i].mode = DTS_MODE_INV;
953	}
954
955	/*Open and Close the Hardware to put it in to sleep state*/
956	crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
957	crystalhd_hw_close(&ctx->hw_ctx);
958	return BC_STS_SUCCESS;
959}
960
961/**
962 * crystalhd_delete_cmd_context - Release Command layer resources.
963 * @ctx: Command layer contextx.
964 *
965 * Return:
966 *	status
967 *
968 * Called at the time of driver un-load.
969 */
970enum BC_STATUS __devexit crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
971{
972	BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
973
974	ctx->adp = NULL;
975
976	return BC_STS_SUCCESS;
977}
978
979/**
980 * crystalhd_get_cmd_proc  - Cproc table lookup.
981 * @ctx: Command layer contextx.
982 * @cmd: IOCTL command code.
983 * @uc: User ID context.
984 *
985 * Return:
986 *	command proc function pointer
987 *
988 * This function checks the process context, application's
989 * mode of operation and returns the function pointer
990 * from the cproc table.
991 */
992crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
993				      struct crystalhd_user *uc)
994{
995	crystalhd_cmd_proc cproc = NULL;
996	unsigned int i, tbl_sz;
997
998	if (!ctx) {
999		BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
1000		return NULL;
1001	}
1002
1003	if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
1004		BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
1005		return NULL;
1006	}
1007
1008	tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(struct crystalhd_cmd_tbl);
1009	for (i = 0; i < tbl_sz; i++) {
1010		if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
1011			if ((uc->mode == DTS_MONITOR_MODE) &&
1012			    (g_crystalhd_cproc_tbl[i].block_mon)) {
1013				BCMLOG(BCMLOG_INFO, "Blocking cmd %d\n", cmd);
1014				break;
1015			}
1016			cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
1017			break;
1018		}
1019	}
1020
1021	return cproc;
1022}
1023
1024/**
1025 * crystalhd_cmd_interrupt - ISR entry point
1026 * @ctx: Command layer contextx.
1027 *
1028 * Return:
1029 *	TRUE: If interrupt from bcm70012 device.
1030 *
1031 *
1032 * ISR entry point from OS layer.
1033 */
1034bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
1035{
1036	if (!ctx) {
1037		BCMLOG_ERR("Invalid arg..\n");
1038		return 0;
1039	}
1040
1041	return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
1042}
1043