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