crystalhd_cmds.c revision 641b63f9981a082eeefab3b395203a54dcd6e93b
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 BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx,
73				      crystalhd_ioctl_data *idata)
74{
75	int rc = 0, i = 0;
76
77	if (!ctx || !idata) {
78		BCMLOG_ERR("Invalid Arg!!\n");
79		return BC_STS_INV_ARG;
80	}
81
82	if (ctx->user[idata->u_id].mode != DTS_MODE_INV) {
83		BCMLOG_ERR("Close the handle first..\n");
84		return BC_STS_ERR_USAGE;
85	}
86	if (idata->udata.u.NotifyMode.Mode == DTS_MONITOR_MODE) {
87		ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
88		return BC_STS_SUCCESS;
89	}
90	if (ctx->state != BC_LINK_INVALID) {
91		BCMLOG_ERR("Link invalid state %d\n", ctx->state);
92		return BC_STS_ERR_USAGE;
93	}
94	/* Check for duplicate playback sessions..*/
95	for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
96		if (ctx->user[i].mode == DTS_DIAG_MODE ||
97		    ctx->user[i].mode == DTS_PLAYBACK_MODE) {
98			BCMLOG_ERR("multiple playback sessions are not "
99				   "supported..\n");
100			return BC_STS_ERR_USAGE;
101		}
102	}
103	ctx->cin_wait_exit = 0;
104	ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
105	/* Setup mmap pool for uaddr sgl mapping..*/
106	rc = crystalhd_create_dio_pool(ctx->adp, BC_LINK_MAX_SGLS);
107	if (rc)
108		return BC_STS_ERROR;
109
110	/* Setup Hardware DMA rings */
111	return crystalhd_hw_setup_dma_rings(&ctx->hw_ctx);
112}
113
114static BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx,
115				      crystalhd_ioctl_data *idata)
116{
117
118	if (!ctx || !idata) {
119		BCMLOG_ERR("Invalid Arg!!\n");
120		return BC_STS_INV_ARG;
121	}
122	idata->udata.u.VerInfo.DriverMajor = crystalhd_kmod_major;
123	idata->udata.u.VerInfo.DriverMinor = crystalhd_kmod_minor;
124	idata->udata.u.VerInfo.DriverRevision	= crystalhd_kmod_rev;
125	return BC_STS_SUCCESS;
126}
127
128
129static BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
130{
131	if (!ctx || !idata) {
132		BCMLOG_ERR("Invalid Arg!!\n");
133		return BC_STS_INV_ARG;
134	}
135
136	crystalhd_pci_cfg_rd(ctx->adp, 0, 2,
137			   (uint32_t *)&idata->udata.u.hwType.PciVenId);
138	crystalhd_pci_cfg_rd(ctx->adp, 2, 2,
139			   (uint32_t *)&idata->udata.u.hwType.PciDevId);
140	crystalhd_pci_cfg_rd(ctx->adp, 8, 1,
141			   (uint32_t *)&idata->udata.u.hwType.HwRev);
142
143	return BC_STS_SUCCESS;
144}
145
146static BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx,
147				 crystalhd_ioctl_data *idata)
148{
149	if (!ctx || !idata)
150		return BC_STS_INV_ARG;
151	idata->udata.u.regAcc.Value = bc_dec_reg_rd(ctx->adp,
152					idata->udata.u.regAcc.Offset);
153	return BC_STS_SUCCESS;
154}
155
156static BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx,
157				 crystalhd_ioctl_data *idata)
158{
159	if (!ctx || !idata)
160		return BC_STS_INV_ARG;
161
162	bc_dec_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
163		      idata->udata.u.regAcc.Value);
164
165	return BC_STS_SUCCESS;
166}
167
168static BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx,
169				      crystalhd_ioctl_data *idata)
170{
171	if (!ctx || !idata)
172		return BC_STS_INV_ARG;
173
174	idata->udata.u.regAcc.Value = crystalhd_reg_rd(ctx->adp,
175					idata->udata.u.regAcc.Offset);
176	return BC_STS_SUCCESS;
177}
178
179static BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx,
180				      crystalhd_ioctl_data *idata)
181{
182	if (!ctx || !idata)
183		return BC_STS_INV_ARG;
184
185	crystalhd_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
186		       idata->udata.u.regAcc.Value);
187
188	return BC_STS_SUCCESS;
189}
190
191static BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx,
192				 crystalhd_ioctl_data *idata)
193{
194	BC_STATUS sts = BC_STS_SUCCESS;
195
196	if (!ctx || !idata || !idata->add_cdata)
197		return BC_STS_INV_ARG;
198
199	if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
200		BCMLOG_ERR("insufficient buffer\n");
201		return BC_STS_INV_ARG;
202	}
203	sts = crystalhd_mem_rd(ctx->adp, idata->udata.u.devMem.StartOff,
204			     idata->udata.u.devMem.NumDwords,
205			     (uint32_t *)idata->add_cdata);
206	return sts;
207
208}
209
210static BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx,
211				 crystalhd_ioctl_data *idata)
212{
213	BC_STATUS sts = BC_STS_SUCCESS;
214
215	if (!ctx || !idata || !idata->add_cdata)
216		return BC_STS_INV_ARG;
217
218	if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
219		BCMLOG_ERR("insufficient buffer\n");
220		return BC_STS_INV_ARG;
221	}
222
223	sts = crystalhd_mem_wr(ctx->adp, idata->udata.u.devMem.StartOff,
224			     idata->udata.u.devMem.NumDwords,
225			     (uint32_t *)idata->add_cdata);
226	return sts;
227}
228
229static BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx,
230				 crystalhd_ioctl_data *idata)
231{
232	uint32_t ix, cnt, off, len;
233	BC_STATUS sts = BC_STS_SUCCESS;
234	uint32_t *temp;
235
236	if (!ctx || !idata)
237		return BC_STS_INV_ARG;
238
239	temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
240	off = idata->udata.u.pciCfg.Offset;
241	len = idata->udata.u.pciCfg.Size;
242
243	if (len <= 4)
244		return crystalhd_pci_cfg_rd(ctx->adp, off, len, temp);
245
246	/* Truncate to dword alignment..*/
247	len = 4;
248	cnt = idata->udata.u.pciCfg.Size / len;
249	for (ix = 0; ix < cnt; ix++) {
250		sts = crystalhd_pci_cfg_rd(ctx->adp, off, len, &temp[ix]);
251		if (sts != BC_STS_SUCCESS) {
252			BCMLOG_ERR("config read : %d\n", sts);
253			return sts;
254		}
255		off += len;
256	}
257
258	return sts;
259}
260
261static BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx,
262				 crystalhd_ioctl_data *idata)
263{
264	uint32_t ix, cnt, off, len;
265	BC_STATUS sts = BC_STS_SUCCESS;
266	uint32_t *temp;
267
268	if (!ctx || !idata)
269		return BC_STS_INV_ARG;
270
271	temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
272	off = idata->udata.u.pciCfg.Offset;
273	len = idata->udata.u.pciCfg.Size;
274
275	if (len <= 4)
276		return crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[0]);
277
278	/* Truncate to dword alignment..*/
279	len = 4;
280	cnt = idata->udata.u.pciCfg.Size / len;
281	for (ix = 0; ix < cnt; ix++) {
282		sts = crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[ix]);
283		if (sts != BC_STS_SUCCESS) {
284			BCMLOG_ERR("config write : %d\n", sts);
285			return sts;
286		}
287		off += len;
288	}
289
290	return sts;
291}
292
293static BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx,
294				      crystalhd_ioctl_data *idata)
295{
296	BC_STATUS sts = BC_STS_SUCCESS;
297
298	if (!ctx || !idata || !idata->add_cdata || !idata->add_cdata_sz) {
299		BCMLOG_ERR("Invalid Arg!!\n");
300		return BC_STS_INV_ARG;
301	}
302
303	if (ctx->state != BC_LINK_INVALID) {
304		BCMLOG_ERR("Link invalid state %d\n", ctx->state);
305		return BC_STS_ERR_USAGE;
306	}
307
308	sts = crystalhd_download_fw(ctx->adp, (uint8_t *)idata->add_cdata,
309				  idata->add_cdata_sz);
310
311	if (sts != BC_STS_SUCCESS) {
312		BCMLOG_ERR("Firmware Download Failure!! - %d\n", sts)
313	} else
314		ctx->state |= BC_LINK_INIT;
315
316	return sts;
317}
318
319/*
320 * We use the FW_CMD interface to sync up playback state with application
321 * and  firmware. This function will perform the required pre and post
322 * processing of the Firmware commands.
323 *
324 * Pause -
325 *	Disable capture after decoder pause.
326 * Resume -
327 *	First enable capture and issue decoder resume command.
328 * Flush -
329 *	Abort pending input transfers and issue decoder flush command.
330 *
331 */
332static BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
333{
334	BC_STATUS sts;
335	uint32_t *cmd;
336
337	if (!(ctx->state & BC_LINK_INIT)) {
338		BCMLOG_ERR("Link invalid state %d\n", ctx->state);
339		return BC_STS_ERR_USAGE;
340	}
341
342	cmd = idata->udata.u.fwCmd.cmd;
343
344	/* Pre-Process */
345	if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
346		if (!cmd[3]) {
347			ctx->state &= ~BC_LINK_PAUSED;
348			crystalhd_hw_unpause(&ctx->hw_ctx);
349		}
350	} else if (cmd[0] == eCMD_C011_DEC_CHAN_FLUSH) {
351		BCMLOG(BCMLOG_INFO, "Flush issued\n");
352		if (cmd[3])
353			ctx->cin_wait_exit = 1;
354	}
355
356	sts = crystalhd_do_fw_cmd(&ctx->hw_ctx, &idata->udata.u.fwCmd);
357
358	if (sts != BC_STS_SUCCESS) {
359		BCMLOG(BCMLOG_INFO, "fw cmd %x failed\n", cmd[0]);
360		return sts;
361	}
362
363	/* Post-Process */
364	if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
365		if (cmd[3]) {
366			ctx->state |= BC_LINK_PAUSED;
367			crystalhd_hw_pause(&ctx->hw_ctx);
368		}
369	}
370
371	return sts;
372}
373
374static void bc_proc_in_completion(crystalhd_dio_req *dio_hnd,
375				  wait_queue_head_t *event, BC_STATUS sts)
376{
377	if (!dio_hnd || !event) {
378		BCMLOG_ERR("Invalid Arg!!\n");
379		return;
380	}
381	if (sts == BC_STS_IO_USER_ABORT)
382		return;
383
384	dio_hnd->uinfo.comp_sts = sts;
385	dio_hnd->uinfo.ev_sts = 1;
386	crystalhd_set_event(event);
387}
388
389static BC_STATUS bc_cproc_codein_sleep(struct crystalhd_cmd *ctx)
390{
391	wait_queue_head_t sleep_ev;
392	int rc = 0;
393
394	if (ctx->state & BC_LINK_SUSPEND)
395		return BC_STS_IO_USER_ABORT;
396
397	if (ctx->cin_wait_exit) {
398		ctx->cin_wait_exit = 0;
399		return BC_STS_CMD_CANCELLED;
400	}
401	crystalhd_create_event(&sleep_ev);
402	crystalhd_wait_on_event(&sleep_ev, 0, 100, rc, 0);
403	if (rc == -EINTR)
404		return BC_STS_IO_USER_ABORT;
405
406	return BC_STS_SUCCESS;
407}
408
409static BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx,
410				   crystalhd_ioctl_data *idata,
411				   crystalhd_dio_req *dio)
412{
413	uint32_t tx_listid = 0;
414	BC_STATUS sts = BC_STS_SUCCESS;
415	wait_queue_head_t event;
416	int rc = 0;
417
418	if (!ctx || !idata || !dio) {
419		BCMLOG_ERR("Invalid Arg!!\n");
420		return BC_STS_INV_ARG;
421	}
422
423	crystalhd_create_event(&event);
424
425	ctx->tx_list_id = 0;
426	/* msleep_interruptible(2000); */
427	sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, bc_proc_in_completion,
428				 &event, &tx_listid,
429				 idata->udata.u.ProcInput.Encrypted);
430
431	while (sts == BC_STS_BUSY) {
432		sts = bc_cproc_codein_sleep(ctx);
433		if (sts != BC_STS_SUCCESS)
434			break;
435		sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio,
436					 bc_proc_in_completion,
437					 &event, &tx_listid,
438					 idata->udata.u.ProcInput.Encrypted);
439	}
440	if (sts != BC_STS_SUCCESS) {
441		BCMLOG(BCMLOG_DBG, "_hw_txdma returning sts:%d\n", sts);
442		return sts;
443	}
444	if (ctx->cin_wait_exit)
445		ctx->cin_wait_exit = 0;
446
447	ctx->tx_list_id = tx_listid;
448
449	/* _post() succeeded.. wait for the completion. */
450	crystalhd_wait_on_event(&event, (dio->uinfo.ev_sts), 3000, rc, 0);
451	ctx->tx_list_id = 0;
452	if (!rc) {
453		return dio->uinfo.comp_sts;
454	} else if (rc == -EBUSY) {
455		BCMLOG(BCMLOG_DBG, "_tx_post() T/O\n");
456		sts = BC_STS_TIMEOUT;
457	} else if (rc == -EINTR) {
458		BCMLOG(BCMLOG_DBG, "Tx Wait Signal int.\n");
459		sts = BC_STS_IO_USER_ABORT;
460	} else {
461		sts = BC_STS_IO_ERROR;
462	}
463
464	/* We are cancelling the IO from the same context as the _post().
465	 * so no need to wait on the event again.. the return itself
466	 * ensures the release of our resources.
467	 */
468	crystalhd_hw_cancel_tx(&ctx->hw_ctx, tx_listid);
469
470	return sts;
471}
472
473/* Helper function to check on user buffers */
474static BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz,
475					uint32_t uv_off, bool en_422)
476{
477	if (!ubuff || !ub_sz) {
478		BCMLOG_ERR("%s->Invalid Arg %p %x\n",
479			((pin) ? "TX" : "RX"), ubuff, ub_sz);
480		return BC_STS_INV_ARG;
481	}
482
483	/* Check for alignment */
484	if (((uintptr_t)ubuff) & 0x03) {
485		BCMLOG_ERR("%s-->Un-aligned address not implemented yet.. %p\n",
486				((pin) ? "TX" : "RX"), ubuff);
487		return BC_STS_NOT_IMPL;
488	}
489	if (pin)
490		return BC_STS_SUCCESS;
491
492	if (!en_422 && !uv_off) {
493		BCMLOG_ERR("Need UV offset for 420 mode.\n");
494		return BC_STS_INV_ARG;
495	}
496
497	if (en_422 && uv_off) {
498		BCMLOG_ERR("UV offset in 422 mode ??\n");
499		return BC_STS_INV_ARG;
500	}
501
502	return BC_STS_SUCCESS;
503}
504
505static BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
506{
507	void *ubuff;
508	uint32_t ub_sz;
509	crystalhd_dio_req *dio_hnd = NULL;
510	BC_STATUS sts = BC_STS_SUCCESS;
511
512	if (!ctx || !idata) {
513		BCMLOG_ERR("Invalid Arg!!\n");
514		return BC_STS_INV_ARG;
515	}
516
517	ubuff = idata->udata.u.ProcInput.pDmaBuff;
518	ub_sz = idata->udata.u.ProcInput.BuffSz;
519
520	sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0);
521	if (sts != BC_STS_SUCCESS)
522		return sts;
523
524	sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd);
525	if (sts != BC_STS_SUCCESS) {
526		BCMLOG_ERR("dio map - %d\n", sts);
527		return sts;
528	}
529
530	if (!dio_hnd)
531		return BC_STS_ERROR;
532
533	sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd);
534
535	crystalhd_unmap_dio(ctx->adp, dio_hnd);
536
537	return sts;
538}
539
540static BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx,
541				       crystalhd_ioctl_data *idata)
542{
543	void *ubuff;
544	uint32_t ub_sz, uv_off;
545	bool en_422;
546	crystalhd_dio_req *dio_hnd = NULL;
547	BC_STATUS sts = BC_STS_SUCCESS;
548
549	if (!ctx || !idata) {
550		BCMLOG_ERR("Invalid Arg!!\n");
551		return BC_STS_INV_ARG;
552	}
553
554	ubuff = idata->udata.u.RxBuffs.YuvBuff;
555	ub_sz = idata->udata.u.RxBuffs.YuvBuffSz;
556	uv_off = idata->udata.u.RxBuffs.UVbuffOffset;
557	en_422 = idata->udata.u.RxBuffs.b422Mode;
558
559	sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422);
560	if (sts != BC_STS_SUCCESS)
561		return sts;
562
563	sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off,
564			      en_422, 0, &dio_hnd);
565	if (sts != BC_STS_SUCCESS) {
566		BCMLOG_ERR("dio map - %d\n", sts);
567		return sts;
568	}
569
570	if (!dio_hnd)
571		return BC_STS_ERROR;
572
573	sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY));
574	if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) {
575		crystalhd_unmap_dio(ctx->adp, dio_hnd);
576		return sts;
577	}
578
579	return BC_STS_SUCCESS;
580}
581
582static BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx,
583				     crystalhd_dio_req *dio)
584{
585	BC_STATUS sts = BC_STS_SUCCESS;
586
587	sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0);
588	if (sts != BC_STS_SUCCESS)
589		return sts;
590
591	ctx->state |= BC_LINK_FMT_CHG;
592	if (ctx->state == BC_LINK_READY)
593		sts = crystalhd_hw_start_capture(&ctx->hw_ctx);
594
595	return sts;
596}
597
598static BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx,
599				      crystalhd_ioctl_data *idata)
600{
601	crystalhd_dio_req *dio = NULL;
602	BC_STATUS sts = BC_STS_SUCCESS;
603	BC_DEC_OUT_BUFF *frame;
604
605	if (!ctx || !idata) {
606		BCMLOG_ERR("Invalid Arg!!\n");
607		return BC_STS_INV_ARG;
608	}
609
610	if (!(ctx->state & BC_LINK_CAP_EN)) {
611		BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state);
612		return BC_STS_ERR_USAGE;
613	}
614
615	frame = &idata->udata.u.DecOutData;
616
617	sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
618	if (sts != BC_STS_SUCCESS)
619		return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts;
620
621	frame->Flags = dio->uinfo.comp_flags;
622
623	if (frame->Flags & COMP_FLAG_FMT_CHANGE)
624		return bc_cproc_fmt_change(ctx, dio);
625
626	frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff;
627	frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len;
628	frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset;
629	frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode;
630
631	frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz;
632	frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz;
633
634	crystalhd_unmap_dio(ctx->adp, dio);
635
636	return BC_STS_SUCCESS;
637}
638
639static BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx,
640					crystalhd_ioctl_data *idata)
641{
642	ctx->state |= BC_LINK_CAP_EN;
643	if (ctx->state == BC_LINK_READY)
644		return crystalhd_hw_start_capture(&ctx->hw_ctx);
645
646	return BC_STS_SUCCESS;
647}
648
649static BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx,
650					  crystalhd_ioctl_data *idata)
651{
652	crystalhd_dio_req *dio = NULL;
653	BC_STATUS sts = BC_STS_SUCCESS;
654	BC_DEC_OUT_BUFF *frame;
655	uint32_t count;
656
657	if (!ctx || !idata) {
658		BCMLOG_ERR("Invalid Arg!!\n");
659		return BC_STS_INV_ARG;
660	}
661
662	if (!(ctx->state & BC_LINK_CAP_EN))
663		return BC_STS_ERR_USAGE;
664
665	/* We should ack flush even when we are in paused/suspend state */
666	if (!(ctx->state & BC_LINK_READY))
667		return crystalhd_hw_stop_capture(&ctx->hw_ctx);
668
669	ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG);
670
671	frame = &idata->udata.u.DecOutData;
672	for (count = 0; count < BC_RX_LIST_CNT; count++) {
673
674		sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
675		if (sts != BC_STS_SUCCESS)
676			break;
677
678		crystalhd_unmap_dio(ctx->adp, dio);
679	}
680
681	return crystalhd_hw_stop_capture(&ctx->hw_ctx);
682}
683
684static BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx,
685				    crystalhd_ioctl_data *idata)
686{
687	BC_DTS_STATS *stats;
688	struct crystalhd_hw_stats	hw_stats;
689
690	if (!ctx || !idata) {
691		BCMLOG_ERR("Invalid Arg!!\n");
692		return BC_STS_INV_ARG;
693	}
694
695	crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats);
696
697	stats = &idata->udata.u.drvStat;
698	stats->drvRLL = hw_stats.rdyq_count;
699	stats->drvFLL = hw_stats.freeq_count;
700	stats->DrvTotalFrmDropped = hw_stats.rx_errors;
701	stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors;
702	stats->intCount = hw_stats.num_interrupts;
703	stats->DrvIgnIntrCnt = hw_stats.num_interrupts -
704				hw_stats.dev_interrupts;
705	stats->TxFifoBsyCnt = hw_stats.cin_busy;
706	stats->pauseCount = hw_stats.pause_cnt;
707
708	if (ctx->pwr_state_change)
709		stats->pwr_state_change = 1;
710	if (ctx->state & BC_LINK_PAUSED)
711		stats->DrvPauseTime = 1;
712
713	return BC_STS_SUCCESS;
714}
715
716static BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx,
717				      crystalhd_ioctl_data *idata)
718{
719	crystalhd_hw_stats(&ctx->hw_ctx, NULL);
720
721	return BC_STS_SUCCESS;
722}
723
724static BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx,
725				  crystalhd_ioctl_data *idata)
726{
727	BC_CLOCK *clock;
728	uint32_t oldClk;
729	BC_STATUS sts = BC_STS_SUCCESS;
730
731	if (!ctx || !idata) {
732		BCMLOG_ERR("Invalid Arg!!\n");
733		return BC_STS_INV_ARG;
734	}
735
736	clock = &idata->udata.u.clockValue;
737	oldClk = ctx->hw_ctx.core_clock_mhz;
738	ctx->hw_ctx.core_clock_mhz = clock->clk;
739
740	if (ctx->state & BC_LINK_READY) {
741		sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx);
742		if (sts == BC_STS_CLK_NOCHG)
743			ctx->hw_ctx.core_clock_mhz = oldClk;
744	}
745
746	clock->clk = ctx->hw_ctx.core_clock_mhz;
747
748	return sts;
749}
750
751/*=============== Cmd Proc Table.. ======================================*/
752static const crystalhd_cmd_tbl_t	g_crystalhd_cproc_tbl[] = {
753	{ BCM_IOC_GET_VERSION,		bc_cproc_get_version,	0},
754	{ BCM_IOC_GET_HWTYPE,		bc_cproc_get_hwtype,	0},
755	{ BCM_IOC_REG_RD,		bc_cproc_reg_rd,	0},
756	{ BCM_IOC_REG_WR,		bc_cproc_reg_wr,	0},
757	{ BCM_IOC_FPGA_RD,		bc_cproc_link_reg_rd,	0},
758	{ BCM_IOC_FPGA_WR,		bc_cproc_link_reg_wr,	0},
759	{ BCM_IOC_MEM_RD,		bc_cproc_mem_rd,	0},
760	{ BCM_IOC_MEM_WR,		bc_cproc_mem_wr,	0},
761	{ BCM_IOC_RD_PCI_CFG,		bc_cproc_cfg_rd,	0},
762	{ BCM_IOC_WR_PCI_CFG,		bc_cproc_cfg_wr,	1},
763	{ BCM_IOC_FW_DOWNLOAD,		bc_cproc_download_fw,	1},
764	{ BCM_IOC_FW_CMD,		bc_cproc_do_fw_cmd,	1},
765	{ BCM_IOC_PROC_INPUT,		bc_cproc_proc_input,	1},
766	{ BCM_IOC_ADD_RXBUFFS,		bc_cproc_add_cap_buff,	1},
767	{ BCM_IOC_FETCH_RXBUFF,		bc_cproc_fetch_frame,	1},
768	{ BCM_IOC_START_RX_CAP,		bc_cproc_start_capture,	1},
769	{ BCM_IOC_FLUSH_RX_CAP,		bc_cproc_flush_cap_buffs, 1},
770	{ BCM_IOC_GET_DRV_STAT,		bc_cproc_get_stats,	0},
771	{ BCM_IOC_RST_DRV_STAT,		bc_cproc_reset_stats,	0},
772	{ BCM_IOC_NOTIFY_MODE,		bc_cproc_notify_mode,	0},
773	{ BCM_IOC_CHG_CLK,		bc_cproc_chg_clk, 0},
774	{ BCM_IOC_END,			NULL},
775};
776
777/*=============== Cmd Proc Functions.. ===================================*/
778
779/**
780 * crystalhd_suspend - Power management suspend request.
781 * @ctx: Command layer context.
782 * @idata: Iodata - required for internal use.
783 *
784 * Return:
785 *	status
786 *
787 * 1. Set the state to Suspend.
788 * 2. Flush the Rx Buffers it will unmap all the buffers and
789 *    stop the RxDMA engine.
790 * 3. Cancel The TX Io and Stop Dma Engine.
791 * 4. Put the DDR in to deep sleep.
792 * 5. Stop the hardware putting it in to Reset State.
793 *
794 * Current gstreamer frame work does not provide any power management
795 * related notification to user mode decoder plug-in. As a work-around
796 * we pass on the power mangement notification to our plug-in by completing
797 * all outstanding requests with BC_STS_IO_USER_ABORT return code.
798 */
799BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
800{
801	BC_STATUS sts = BC_STS_SUCCESS;
802
803	if (!ctx || !idata) {
804		BCMLOG_ERR("Invalid Parameters\n");
805		return BC_STS_ERROR;
806	}
807
808	if (ctx->state & BC_LINK_SUSPEND)
809		return BC_STS_SUCCESS;
810
811	if (ctx->state == BC_LINK_INVALID) {
812		BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
813		return BC_STS_SUCCESS;
814	}
815
816	ctx->state |= BC_LINK_SUSPEND;
817
818	bc_cproc_mark_pwr_state(ctx);
819
820	if (ctx->state & BC_LINK_CAP_EN) {
821		sts = bc_cproc_flush_cap_buffs(ctx, idata);
822		if (sts != BC_STS_SUCCESS)
823			return sts;
824	}
825
826	if (ctx->tx_list_id) {
827		sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
828		if (sts != BC_STS_SUCCESS)
829			return sts;
830	}
831
832	sts = crystalhd_hw_suspend(&ctx->hw_ctx);
833	if (sts != BC_STS_SUCCESS)
834		return sts;
835
836	BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
837
838	return BC_STS_SUCCESS;
839}
840
841/**
842 * crystalhd_resume - Resume frame capture.
843 * @ctx: Command layer contextx.
844 *
845 * Return:
846 *	status
847 *
848 *
849 * Resume frame capture.
850 *
851 * PM_Resume can't resume the playback state back to pre-suspend state
852 * because we don't keep video clip related information within driver.
853 * To get back to the pre-suspend state App will re-open the device and
854 * start a new playback session from the pre-suspend clip position.
855 *
856 */
857BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
858{
859	BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
860
861	bc_cproc_mark_pwr_state(ctx);
862
863	return BC_STS_SUCCESS;
864}
865
866/**
867 * crystalhd_user_open - Create application handle.
868 * @ctx: Command layer contextx.
869 * @user_ctx: User ID context.
870 *
871 * Return:
872 *	status
873 *
874 * Creates an application specific UID and allocates
875 * application specific resources. HW layer initialization
876 * is done for the first open request.
877 */
878BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
879			    struct crystalhd_user **user_ctx)
880{
881	struct crystalhd_user *uc;
882
883	if (!ctx || !user_ctx) {
884		BCMLOG_ERR("Invalid arg..\n");
885		return BC_STS_INV_ARG;
886	}
887
888	uc = bc_cproc_get_uid(ctx);
889	if (!uc) {
890		BCMLOG(BCMLOG_INFO, "No free user context...\n");
891		return BC_STS_BUSY;
892	}
893
894	BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
895
896	crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
897
898	uc->in_use = 1;
899
900	*user_ctx = uc;
901
902	return BC_STS_SUCCESS;
903}
904
905/**
906 * crystalhd_user_close - Close application handle.
907 * @ctx: Command layer contextx.
908 * @uc: User ID context.
909 *
910 * Return:
911 *	status
912 *
913 * Closer aplication handle and release app specific
914 * resources.
915 */
916BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
917{
918	uint32_t mode = uc->mode;
919
920	ctx->user[uc->uid].mode = DTS_MODE_INV;
921	ctx->user[uc->uid].in_use = 0;
922	ctx->cin_wait_exit = 1;
923	ctx->pwr_state_change = 0;
924
925	BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
926
927	if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
928		crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
929		crystalhd_destroy_dio_pool(ctx->adp);
930	} else if (bc_cproc_get_user_count(ctx)) {
931		return BC_STS_SUCCESS;
932	}
933
934	crystalhd_hw_close(&ctx->hw_ctx);
935
936	ctx->state = BC_LINK_INVALID;
937
938	return BC_STS_SUCCESS;
939}
940
941/**
942 * crystalhd_setup_cmd_context - Setup Command layer resources.
943 * @ctx: Command layer contextx.
944 * @adp: Adapter context
945 *
946 * Return:
947 *	status
948 *
949 * Called at the time of driver load.
950 */
951BC_STATUS __devinit crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
952				    struct crystalhd_adp *adp)
953{
954	int i = 0;
955
956	if (!ctx || !adp) {
957		BCMLOG_ERR("Invalid arg!!\n");
958		return BC_STS_INV_ARG;
959	}
960
961	if (ctx->adp)
962		BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
963
964	ctx->adp = adp;
965	for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
966		ctx->user[i].uid = i;
967		ctx->user[i].in_use = 0;
968		ctx->user[i].mode = DTS_MODE_INV;
969	}
970
971	/*Open and Close the Hardware to put it in to sleep state*/
972	crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
973	crystalhd_hw_close(&ctx->hw_ctx);
974	return BC_STS_SUCCESS;
975}
976
977/**
978 * crystalhd_delete_cmd_context - Release Command layer resources.
979 * @ctx: Command layer contextx.
980 *
981 * Return:
982 *	status
983 *
984 * Called at the time of driver un-load.
985 */
986BC_STATUS __devexit crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
987{
988	BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
989
990	ctx->adp = NULL;
991
992	return BC_STS_SUCCESS;
993}
994
995/**
996 * crystalhd_get_cmd_proc  - Cproc table lookup.
997 * @ctx: Command layer contextx.
998 * @cmd: IOCTL command code.
999 * @uc: User ID context.
1000 *
1001 * Return:
1002 *	command proc function pointer
1003 *
1004 * This function checks the process context, application's
1005 * mode of operation and returns the function pointer
1006 * from the cproc table.
1007 */
1008crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
1009				      struct crystalhd_user *uc)
1010{
1011	crystalhd_cmd_proc cproc = NULL;
1012	unsigned int i, tbl_sz;
1013
1014	if (!ctx) {
1015		BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
1016		return NULL;
1017	}
1018
1019	if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
1020		BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
1021		return NULL;
1022	}
1023
1024	tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(crystalhd_cmd_tbl_t);
1025	for (i = 0; i < tbl_sz; i++) {
1026		if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
1027			if ((uc->mode == DTS_MONITOR_MODE) &&
1028			    (g_crystalhd_cproc_tbl[i].block_mon)) {
1029				BCMLOG(BCMLOG_INFO, "Blocking cmd %d\n", cmd);
1030				break;
1031			}
1032			cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
1033			break;
1034		}
1035	}
1036
1037	return cproc;
1038}
1039
1040/**
1041 * crystalhd_cmd_interrupt - ISR entry point
1042 * @ctx: Command layer contextx.
1043 *
1044 * Return:
1045 *	TRUE: If interrupt from bcm70012 device.
1046 *
1047 *
1048 * ISR entry point from OS layer.
1049 */
1050bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
1051{
1052	if (!ctx) {
1053		BCMLOG_ERR("Invalid arg..\n");
1054		return 0;
1055	}
1056
1057	return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
1058}
1059