• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
bc_cproc_get_uid(struct crystalhd_cmd * ctx)29 static 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 
bc_cproc_get_user_count(struct crystalhd_cmd * ctx)44 static 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 
bc_cproc_mark_pwr_state(struct crystalhd_cmd * ctx)56 static 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 
bc_cproc_notify_mode(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)71 static 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 
bc_cproc_get_version(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)113 static 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 
bc_cproc_get_hwtype(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)128 static 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 
bc_cproc_reg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)146 static 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 
bc_cproc_reg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)156 static 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 
bc_cproc_link_reg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)168 static 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 
bc_cproc_link_reg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)179 static 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 
bc_cproc_mem_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)191 static 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 
bc_cproc_mem_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)210 static 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 
bc_cproc_cfg_rd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)229 static 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 
bc_cproc_cfg_wr(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)261 static 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 
bc_cproc_download_fw(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)293 static 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  */
bc_cproc_do_fw_cmd(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)332 static 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 
bc_proc_in_completion(struct crystalhd_dio_req * dio_hnd,wait_queue_head_t * event,enum BC_STATUS sts)375 static 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 
bc_cproc_codein_sleep(struct crystalhd_cmd * ctx)390 static 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 
bc_cproc_hw_txdma(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata,struct crystalhd_dio_req * dio)410 static 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 */
bc_cproc_check_inbuffs(bool pin,void * ubuff,uint32_t ub_sz,uint32_t uv_off,bool en_422)475 static 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 
bc_cproc_proc_input(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)506 static 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 
bc_cproc_add_cap_buff(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)542 static 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 
bc_cproc_fmt_change(struct crystalhd_cmd * ctx,struct crystalhd_dio_req * dio)584 static 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 
bc_cproc_fetch_frame(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)600 static 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 
bc_cproc_start_capture(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)641 static 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 
bc_cproc_flush_cap_buffs(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)651 static 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 
bc_cproc_get_stats(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)686 static 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 
bc_cproc_reset_stats(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)718 static 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 
bc_cproc_chg_clk(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)726 static 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.. ======================================*/
754 static 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  */
crystalhd_suspend(struct crystalhd_cmd * ctx,struct crystalhd_ioctl_data * idata)801 enum 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  */
crystalhd_resume(struct crystalhd_cmd * ctx)860 enum 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  */
crystalhd_user_open(struct crystalhd_cmd * ctx,struct crystalhd_user ** user_ctx)881 enum 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  */
crystalhd_user_close(struct crystalhd_cmd * ctx,struct crystalhd_user * uc)919 enum 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  */
crystalhd_setup_cmd_context(struct crystalhd_cmd * ctx,struct crystalhd_adp * adp)954 enum BC_STATUS 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  */
crystalhd_delete_cmd_context(struct crystalhd_cmd * ctx)989 enum BC_STATUS 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  */
crystalhd_get_cmd_proc(struct crystalhd_cmd * ctx,uint32_t cmd,struct crystalhd_user * uc)1011 crystalhd_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  */
crystalhd_cmd_interrupt(struct crystalhd_cmd * ctx)1053 bool 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