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
29static 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
44static 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
56static 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
71static 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
113static 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
128static 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
146static 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
156static 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
168static 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
179static 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
191static 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
210static 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
229static 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
261static 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
293static 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 */
332static 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
375static 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
390static 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
410static 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 */
475static 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
506static 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
542static 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
584static 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
600static 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
641static 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
651static 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
686static 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
718static 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
726static 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.. ======================================*/
754static 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 */
801enum 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 */
860enum 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 */
881enum 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 */
919enum 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 */
954enum BC_STATUS __devinit 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 */
989enum BC_STATUS __devexit 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 */
1011crystalhd_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 */
1053bool 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