strm.c 21.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * strm.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * DSP/BIOS Bridge Stream Manager.
 *
 * Copyright (C) 2005-2006 Texas Instruments, Inc.
 *
 * This package is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

19 20
#include <linux/types.h>

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/*  ----------------------------------- Host OS */
#include <dspbridge/host_os.h>

/*  ----------------------------------- DSP/BIOS Bridge */
#include <dspbridge/dbdefs.h>

/*  ----------------------------------- Trace & Debug */
#include <dspbridge/dbc.h>

/*  ----------------------------------- OS Adaptation Layer */
#include <dspbridge/sync.h>

/*  ----------------------------------- Bridge Driver */
#include <dspbridge/dspdefs.h>

/*  ----------------------------------- Resource Manager */
#include <dspbridge/nodepriv.h>

/*  ----------------------------------- Others */
#include <dspbridge/cmm.h>

/*  ----------------------------------- This */
#include <dspbridge/strm.h>

#include <dspbridge/cfg.h>
#include <dspbridge/resourcecleanup.h>

/*  ----------------------------------- Defines, Data Structures, Typedefs */
#define DEFAULTTIMEOUT      10000
#define DEFAULTNUMBUFS      2

/*
 *  ======== strm_mgr ========
 *  The strm_mgr contains device information needed to open the underlying
 *  channels of a stream.
 */
struct strm_mgr {
	struct dev_object *dev_obj;	/* Device for this processor */
	struct chnl_mgr *hchnl_mgr;	/* Channel manager */
	/* Function interface to Bridge driver */
	struct bridge_drv_interface *intf_fxns;
};

/*
 *  ======== strm_object ========
 *  This object is allocated in strm_open().
 */
struct strm_object {
	struct strm_mgr *strm_mgr_obj;
	struct chnl_object *chnl_obj;
	u32 dir;		/* DSP_TONODE or DSP_FROMNODE */
	u32 utimeout;
	u32 num_bufs;		/* Max # of bufs allowed in stream */
	u32 un_bufs_in_strm;	/* Current # of bufs in stream */
	u32 ul_n_bytes;		/* bytes transferred since idled */
	/* STREAM_IDLE, STREAM_READY, ... */
	enum dsp_streamstate strm_state;
	void *user_event;	/* Saved for strm_get_info() */
	enum dsp_strmmode strm_mode;	/* STRMMODE_[PROCCOPY][ZEROCOPY]... */
	u32 udma_chnl_id;	/* DMA chnl id */
	u32 udma_priority;	/* DMA priority:DMAPRI_[LOW][HIGH] */
	u32 segment_id;		/* >0 is SM segment.=0 is local heap */
	u32 buf_alignment;	/* Alignment for stream bufs */
	/* Stream's SM address translator */
	struct cmm_xlatorobject *xlator;
};

/*  ----------------------------------- Globals */
static u32 refs;		/* module reference count */

/*  ----------------------------------- Function Prototypes */
92
static int delete_strm(struct strm_object *stream_obj);
93 94 95 96 97 98

/*
 *  ======== strm_allocate_buffer ========
 *  Purpose:
 *      Allocates buffers for a stream.
 */
99
int strm_allocate_buffer(struct strm_res_object *strmres, u32 usize,
100
				u8 **ap_buffer, u32 num_bufs,
101 102 103 104 105
				struct process_context *pr_ctxt)
{
	int status = 0;
	u32 alloc_cnt = 0;
	u32 i;
106
	struct strm_object *stream_obj = strmres->hstream;
107 108 109 110

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(ap_buffer != NULL);

111
	if (stream_obj) {
112 113 114 115 116 117 118 119 120 121
		/*
		 * Allocate from segment specified at time of stream open.
		 */
		if (usize == 0)
			status = -EINVAL;

	} else {
		status = -EFAULT;
	}

122
	if (status)
123 124 125
		goto func_end;

	for (i = 0; i < num_bufs; i++) {
126 127 128
		DBC_ASSERT(stream_obj->xlator != NULL);
		(void)cmm_xlator_alloc_buf(stream_obj->xlator, &ap_buffer[i],
					   usize);
129 130 131 132 133 134
		if (ap_buffer[i] == NULL) {
			status = -ENOMEM;
			alloc_cnt = i;
			break;
		}
	}
135
	if (status)
136
		strm_free_buffer(strmres, ap_buffer, alloc_cnt, pr_ctxt);
137

138
	if (status)
139 140
		goto func_end;

141
	drv_proc_update_strm_res(num_bufs, strmres);
142 143 144 145 146 147 148 149 150 151

func_end:
	return status;
}

/*
 *  ======== strm_close ========
 *  Purpose:
 *      Close a stream opened with strm_open().
 */
152
int strm_close(struct strm_res_object *strmres,
153 154 155 156 157
		      struct process_context *pr_ctxt)
{
	struct bridge_drv_interface *intf_fxns;
	struct chnl_info chnl_info_obj;
	int status = 0;
158
	struct strm_object *stream_obj = strmres->hstream;
159 160 161

	DBC_REQUIRE(refs > 0);

162
	if (!stream_obj) {
163 164 165 166
		status = -EFAULT;
	} else {
		/* Have all buffers been reclaimed? If not, return
		 * -EPIPE */
167
		intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
168
		status =
169
		    (*intf_fxns->pfn_chnl_get_info) (stream_obj->chnl_obj,
170
						     &chnl_info_obj);
171
		DBC_ASSERT(!status);
172 173 174 175

		if (chnl_info_obj.cio_cs > 0 || chnl_info_obj.cio_reqs > 0)
			status = -EPIPE;
		else
176
			status = delete_strm(stream_obj);
177 178
	}

179
	if (status)
180 181
		goto func_end;

182
	idr_remove(pr_ctxt->stream_id, strmres->id);
183 184 185 186
func_end:
	DBC_ENSURE(status == 0 || status == -EFAULT ||
		   status == -EPIPE || status == -EPERM);

187 188
	dev_dbg(bridge, "%s: stream_obj: %p, status 0x%x\n", __func__,
		stream_obj, status);
189 190 191 192 193 194 195 196
	return status;
}

/*
 *  ======== strm_create ========
 *  Purpose:
 *      Create a STRM manager object.
 */
197
int strm_create(struct strm_mgr **strm_man,
198 199 200 201 202 203
		       struct dev_object *dev_obj)
{
	struct strm_mgr *strm_mgr_obj;
	int status = 0;

	DBC_REQUIRE(refs > 0);
204
	DBC_REQUIRE(strm_man != NULL);
205 206
	DBC_REQUIRE(dev_obj != NULL);

207
	*strm_man = NULL;
208 209 210 211 212 213 214 215
	/* Allocate STRM manager object */
	strm_mgr_obj = kzalloc(sizeof(struct strm_mgr), GFP_KERNEL);
	if (strm_mgr_obj == NULL)
		status = -ENOMEM;
	else
		strm_mgr_obj->dev_obj = dev_obj;

	/* Get Channel manager and Bridge function interface */
216
	if (!status) {
217
		status = dev_get_chnl_mgr(dev_obj, &(strm_mgr_obj->hchnl_mgr));
218
		if (!status) {
219 220 221 222 223 224
			(void)dev_get_intf_fxns(dev_obj,
						&(strm_mgr_obj->intf_fxns));
			DBC_ASSERT(strm_mgr_obj->intf_fxns != NULL);
		}
	}

225
	if (!status)
226
		*strm_man = strm_mgr_obj;
227
	else
228
		kfree(strm_mgr_obj);
229

230
	DBC_ENSURE((!status && *strm_man) || (status && *strm_man == NULL));
231 232 233 234 235 236 237 238 239 240 241 242 243 244

	return status;
}

/*
 *  ======== strm_delete ========
 *  Purpose:
 *      Delete the STRM Manager Object.
 */
void strm_delete(struct strm_mgr *strm_mgr_obj)
{
	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(strm_mgr_obj);

245
	kfree(strm_mgr_obj);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
}

/*
 *  ======== strm_exit ========
 *  Purpose:
 *      Discontinue usage of STRM module.
 */
void strm_exit(void)
{
	DBC_REQUIRE(refs > 0);

	refs--;

	DBC_ENSURE(refs >= 0);
}

/*
 *  ======== strm_free_buffer ========
 *  Purpose:
 *      Frees the buffers allocated for a stream.
 */
267
int strm_free_buffer(struct strm_res_object *strmres, u8 ** ap_buffer,
268 269 270 271
			    u32 num_bufs, struct process_context *pr_ctxt)
{
	int status = 0;
	u32 i = 0;
272
	struct strm_object *stream_obj = strmres->hstream;
273 274 275 276

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(ap_buffer != NULL);

277
	if (!stream_obj)
278 279
		status = -EFAULT;

280
	if (!status) {
281
		for (i = 0; i < num_bufs; i++) {
282
			DBC_ASSERT(stream_obj->xlator != NULL);
283
			status =
284 285
			    cmm_xlator_free_buf(stream_obj->xlator,
						ap_buffer[i]);
286
			if (status)
287 288 289 290
				break;
			ap_buffer[i] = NULL;
		}
	}
291
	drv_proc_update_strm_res(num_bufs - i, strmres);
292 293 294 295 296 297 298 299 300

	return status;
}

/*
 *  ======== strm_get_info ========
 *  Purpose:
 *      Retrieves information about a stream.
 */
301
int strm_get_info(struct strm_object *stream_obj,
302
			 struct stream_info *stream_info,
303 304 305 306 307 308 309 310 311 312 313
			 u32 stream_info_size)
{
	struct bridge_drv_interface *intf_fxns;
	struct chnl_info chnl_info_obj;
	int status = 0;
	void *virt_base = NULL;	/* NULL if no SM used */

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(stream_info != NULL);
	DBC_REQUIRE(stream_info_size >= sizeof(struct stream_info));

314
	if (!stream_obj) {
315 316 317 318 319 320 321
		status = -EFAULT;
	} else {
		if (stream_info_size < sizeof(struct stream_info)) {
			/* size of users info */
			status = -EINVAL;
		}
	}
322
	if (status)
323 324
		goto func_end;

325
	intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
326
	status =
327 328
	    (*intf_fxns->pfn_chnl_get_info) (stream_obj->chnl_obj,
						  &chnl_info_obj);
329
	if (status)
330 331
		goto func_end;

332
	if (stream_obj->xlator) {
333
		/* We have a translator */
334 335 336
		DBC_ASSERT(stream_obj->segment_id > 0);
		cmm_xlator_info(stream_obj->xlator, (u8 **) &virt_base, 0,
				stream_obj->segment_id, false);
337
	}
338 339
	stream_info->segment_id = stream_obj->segment_id;
	stream_info->strm_mode = stream_obj->strm_mode;
340
	stream_info->virt_base = virt_base;
341
	stream_info->user_strm->number_bufs_allowed = stream_obj->num_bufs;
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	stream_info->user_strm->number_bufs_in_stream = chnl_info_obj.cio_cs +
	    chnl_info_obj.cio_reqs;
	/* # of bytes transferred since last call to DSPStream_Idle() */
	stream_info->user_strm->ul_number_bytes = chnl_info_obj.bytes_tx;
	stream_info->user_strm->sync_object_handle = chnl_info_obj.event_obj;
	/* Determine stream state based on channel state and info */
	if (chnl_info_obj.dw_state & CHNL_STATEEOS) {
		stream_info->user_strm->ss_stream_state = STREAM_DONE;
	} else {
		if (chnl_info_obj.cio_cs > 0)
			stream_info->user_strm->ss_stream_state = STREAM_READY;
		else if (chnl_info_obj.cio_reqs > 0)
			stream_info->user_strm->ss_stream_state =
			    STREAM_PENDING;
		else
			stream_info->user_strm->ss_stream_state = STREAM_IDLE;

	}
func_end:
	return status;
}

/*
 *  ======== strm_idle ========
 *  Purpose:
 *      Idles a particular stream.
 */
369
int strm_idle(struct strm_object *stream_obj, bool flush_data)
370 371 372 373 374 375
{
	struct bridge_drv_interface *intf_fxns;
	int status = 0;

	DBC_REQUIRE(refs > 0);

376
	if (!stream_obj) {
377 378
		status = -EFAULT;
	} else {
379
		intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
380

381 382
		status = (*intf_fxns->pfn_chnl_idle) (stream_obj->chnl_obj,
						      stream_obj->utimeout,
383
						      flush_data);
384 385
	}

386 387
	dev_dbg(bridge, "%s: stream_obj: %p flush_data: 0x%x status: 0x%x\n",
		__func__, stream_obj, flush_data, status);
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
	return status;
}

/*
 *  ======== strm_init ========
 *  Purpose:
 *      Initialize the STRM module.
 */
bool strm_init(void)
{
	bool ret = true;

	DBC_REQUIRE(refs >= 0);

	if (ret)
		refs++;

	DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs >= 0)));

	return ret;
}

/*
 *  ======== strm_issue ========
 *  Purpose:
 *      Issues a buffer on a stream
 */
415
int strm_issue(struct strm_object *stream_obj, u8 *pbuf, u32 ul_bytes,
416 417 418 419 420 421 422 423 424
		      u32 ul_buf_size, u32 dw_arg)
{
	struct bridge_drv_interface *intf_fxns;
	int status = 0;
	void *tmp_buf = NULL;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(pbuf != NULL);

425
	if (!stream_obj) {
426 427
		status = -EFAULT;
	} else {
428
		intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
429

430 431
		if (stream_obj->segment_id != 0) {
			tmp_buf = cmm_xlator_translate(stream_obj->xlator,
432 433 434 435 436 437
						       (void *)pbuf,
						       CMM_VA2DSPPA);
			if (tmp_buf == NULL)
				status = -ESRCH;

		}
438
		if (!status) {
439
			status = (*intf_fxns->pfn_chnl_add_io_req)
440
			    (stream_obj->chnl_obj, pbuf, ul_bytes, ul_buf_size,
441 442 443 444 445 446
			     (u32) tmp_buf, dw_arg);
		}
		if (status == -EIO)
			status = -ENOSR;
	}

447 448
	dev_dbg(bridge, "%s: stream_obj: %p pbuf: %p ul_bytes: 0x%x dw_arg:"
		" 0x%x status: 0x%x\n", __func__, stream_obj, pbuf,
449 450 451 452 453 454 455 456 457 458 459
		ul_bytes, dw_arg, status);
	return status;
}

/*
 *  ======== strm_open ========
 *  Purpose:
 *      Open a stream for sending/receiving data buffers to/from a task or
 *      XDAIS socket node on the DSP.
 */
int strm_open(struct node_object *hnode, u32 dir, u32 index,
460
		     struct strm_attr *pattr,
461
		     struct strm_res_object **strmres,
462 463 464 465 466 467 468 469 470 471 472
		     struct process_context *pr_ctxt)
{
	struct strm_mgr *strm_mgr_obj;
	struct bridge_drv_interface *intf_fxns;
	u32 ul_chnl_id;
	struct strm_object *strm_obj = NULL;
	s8 chnl_mode;
	struct chnl_attr chnl_attr_obj;
	int status = 0;
	struct cmm_object *hcmm_mgr = NULL;	/* Shared memory manager hndl */

473
	void *stream_res;
474 475

	DBC_REQUIRE(refs > 0);
476
	DBC_REQUIRE(strmres != NULL);
477
	DBC_REQUIRE(pattr != NULL);
478
	*strmres = NULL;
479 480 481 482 483 484
	if (dir != DSP_TONODE && dir != DSP_FROMNODE) {
		status = -EPERM;
	} else {
		/* Get the channel id from the node (set in node_connect()) */
		status = node_get_channel_id(hnode, dir, index, &ul_chnl_id);
	}
485
	if (!status)
486 487
		status = node_get_strm_mgr(hnode, &strm_mgr_obj);

488
	if (!status) {
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
		strm_obj = kzalloc(sizeof(struct strm_object), GFP_KERNEL);
		if (strm_obj == NULL) {
			status = -ENOMEM;
		} else {
			strm_obj->strm_mgr_obj = strm_mgr_obj;
			strm_obj->dir = dir;
			strm_obj->strm_state = STREAM_IDLE;
			strm_obj->user_event = pattr->user_event;
			if (pattr->stream_attr_in != NULL) {
				strm_obj->utimeout =
				    pattr->stream_attr_in->utimeout;
				strm_obj->num_bufs =
				    pattr->stream_attr_in->num_bufs;
				strm_obj->strm_mode =
				    pattr->stream_attr_in->strm_mode;
				strm_obj->segment_id =
				    pattr->stream_attr_in->segment_id;
				strm_obj->buf_alignment =
				    pattr->stream_attr_in->buf_alignment;
				strm_obj->udma_chnl_id =
				    pattr->stream_attr_in->udma_chnl_id;
				strm_obj->udma_priority =
				    pattr->stream_attr_in->udma_priority;
				chnl_attr_obj.uio_reqs =
				    pattr->stream_attr_in->num_bufs;
			} else {
				strm_obj->utimeout = DEFAULTTIMEOUT;
				strm_obj->num_bufs = DEFAULTNUMBUFS;
				strm_obj->strm_mode = STRMMODE_PROCCOPY;
				strm_obj->segment_id = 0;	/* local mem */
				strm_obj->buf_alignment = 0;
				strm_obj->udma_chnl_id = 0;
				strm_obj->udma_priority = 0;
				chnl_attr_obj.uio_reqs = DEFAULTNUMBUFS;
			}
			chnl_attr_obj.reserved1 = NULL;
			/* DMA chnl flush timeout */
			chnl_attr_obj.reserved2 = strm_obj->utimeout;
			chnl_attr_obj.event_obj = NULL;
			if (pattr->user_event != NULL)
				chnl_attr_obj.event_obj = pattr->user_event;

		}
	}
533
	if (status)
534 535 536 537 538 539 540 541 542
		goto func_cont;

	if ((pattr->virt_base == NULL) || !(pattr->ul_virt_size > 0))
		goto func_cont;

	/* No System DMA */
	DBC_ASSERT(strm_obj->strm_mode != STRMMODE_LDMA);
	/* Get the shared mem mgr for this streams dev object */
	status = dev_get_cmm_mgr(strm_mgr_obj->dev_obj, &hcmm_mgr);
543
	if (!status) {
544 545
		/*Allocate a SM addr translator for this strm. */
		status = cmm_xlator_create(&strm_obj->xlator, hcmm_mgr, NULL);
546
		if (!status) {
547 548 549 550 551 552 553 554 555
			DBC_ASSERT(strm_obj->segment_id > 0);
			/*  Set translators Virt Addr attributes */
			status = cmm_xlator_info(strm_obj->xlator,
						 (u8 **) &pattr->virt_base,
						 pattr->ul_virt_size,
						 strm_obj->segment_id, true);
		}
	}
func_cont:
556
	if (!status) {
557 558 559 560 561 562 563 564
		/* Open channel */
		chnl_mode = (dir == DSP_TONODE) ?
		    CHNL_MODETODSP : CHNL_MODEFROMDSP;
		intf_fxns = strm_mgr_obj->intf_fxns;
		status = (*intf_fxns->pfn_chnl_open) (&(strm_obj->chnl_obj),
						      strm_mgr_obj->hchnl_mgr,
						      chnl_mode, ul_chnl_id,
						      &chnl_attr_obj);
565
		if (status) {
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
			/*
			 * over-ride non-returnable status codes so we return
			 * something documented
			 */
			if (status != -ENOMEM && status !=
			    -EINVAL && status != -EPERM) {
				/*
				 * We got a status that's not return-able.
				 * Assert that we got something we were
				 * expecting (-EFAULT isn't acceptable,
				 * strm_mgr_obj->hchnl_mgr better be valid or we
				 * assert here), and then return -EPERM.
				 */
				DBC_ASSERT(status == -ENOSR ||
					   status == -ECHRNG ||
					   status == -EALREADY ||
					   status == -EIO);
				status = -EPERM;
			}
		}
	}
587
	if (!status) {
588 589 590 591 592 593
		status = drv_proc_insert_strm_res_element(strm_obj,
							&stream_res, pr_ctxt);
		if (status)
			delete_strm(strm_obj);
		else
			*strmres = (struct strm_res_object *)stream_res;
594 595 596 597 598
	} else {
		(void)delete_strm(strm_obj);
	}

	/* ensure we return a documented error code */
599 600
	DBC_ENSURE((!status && strm_obj) ||
		   (*strmres == NULL && (status == -EFAULT ||
601 602 603 604
					status == -EPERM
					|| status == -EINVAL)));

	dev_dbg(bridge, "%s: hnode: %p dir: 0x%x index: 0x%x pattr: %p "
605 606
		"strmres: %p status: 0x%x\n", __func__,
		hnode, dir, index, pattr, strmres, status);
607 608 609 610 611 612 613 614
	return status;
}

/*
 *  ======== strm_reclaim ========
 *  Purpose:
 *      Relcaims a buffer from a stream.
 */
615
int strm_reclaim(struct strm_object *stream_obj, u8 ** buf_ptr,
616
			u32 *nbytes, u32 *buff_size, u32 *pdw_arg)
617 618 619 620 621 622 623 624
{
	struct bridge_drv_interface *intf_fxns;
	struct chnl_ioc chnl_ioc_obj;
	int status = 0;
	void *tmp_buf = NULL;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(buf_ptr != NULL);
625
	DBC_REQUIRE(nbytes != NULL);
626 627
	DBC_REQUIRE(pdw_arg != NULL);

628
	if (!stream_obj) {
629 630 631
		status = -EFAULT;
		goto func_end;
	}
632
	intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
633 634

	status =
635 636
	    (*intf_fxns->pfn_chnl_get_ioc) (stream_obj->chnl_obj,
					    stream_obj->utimeout,
637
					    &chnl_ioc_obj);
638
	if (!status) {
639 640 641
		*nbytes = chnl_ioc_obj.byte_size;
		if (buff_size)
			*buff_size = chnl_ioc_obj.buf_size;
642 643 644 645 646 647 648 649 650 651 652 653 654

		*pdw_arg = chnl_ioc_obj.dw_arg;
		if (!CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) {
			if (CHNL_IS_TIMED_OUT(chnl_ioc_obj)) {
				status = -ETIME;
			} else {
				/* Allow reclaims after idle to succeed */
				if (!CHNL_IS_IO_CANCELLED(chnl_ioc_obj))
					status = -EPERM;

			}
		}
		/* Translate zerocopy buffer if channel not canceled. */
655
		if (!status
656
		    && (!CHNL_IS_IO_CANCELLED(chnl_ioc_obj))
657
		    && (stream_obj->strm_mode == STRMMODE_ZEROCOPY)) {
658 659 660 661 662 663 664
			/*
			 *  This is a zero-copy channel so chnl_ioc_obj.pbuf
			 *  contains the DSP address of SM. We need to
			 *  translate it to a virtual address for the user
			 *  thread to access.
			 *  Note: Could add CMM_DSPPA2VA to CMM in the future.
			 */
665
			tmp_buf = cmm_xlator_translate(stream_obj->xlator,
666 667 668 669
						       chnl_ioc_obj.pbuf,
						       CMM_DSPPA2PA);
			if (tmp_buf != NULL) {
				/* now convert this GPP Pa to Va */
670 671
				tmp_buf = cmm_xlator_translate(stream_obj->
							       xlator,
672 673 674 675 676 677 678 679 680 681 682 683
							       tmp_buf,
							       CMM_PA2VA);
			}
			if (tmp_buf == NULL)
				status = -ESRCH;

			chnl_ioc_obj.pbuf = tmp_buf;
		}
		*buf_ptr = chnl_ioc_obj.pbuf;
	}
func_end:
	/* ensure we return a documented return code */
684
	DBC_ENSURE(!status || status == -EFAULT ||
685 686 687
		   status == -ETIME || status == -ESRCH ||
		   status == -EPERM);

688
	dev_dbg(bridge, "%s: stream_obj: %p buf_ptr: %p nbytes: %p "
689
		"pdw_arg: %p status 0x%x\n", __func__, stream_obj,
690
		buf_ptr, nbytes, pdw_arg, status);
691 692 693 694 695 696 697 698
	return status;
}

/*
 *  ======== strm_register_notify ========
 *  Purpose:
 *      Register to be notified on specific events for this stream.
 */
699
int strm_register_notify(struct strm_object *stream_obj, u32 event_mask,
700 701 702 703 704 705 706 707 708
				u32 notify_type, struct dsp_notification
				* hnotification)
{
	struct bridge_drv_interface *intf_fxns;
	int status = 0;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(hnotification != NULL);

709
	if (!stream_obj) {
710 711 712 713 714 715 716 717 718
		status = -EFAULT;
	} else if ((event_mask & ~((DSP_STREAMIOCOMPLETION) |
				   DSP_STREAMDONE)) != 0) {
		status = -EINVAL;
	} else {
		if (notify_type != DSP_SIGNALEVENT)
			status = -ENOSYS;

	}
719
	if (!status) {
720
		intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
721 722

		status =
723 724
		    (*intf_fxns->pfn_chnl_register_notify) (stream_obj->
							    chnl_obj,
725 726 727 728 729
							    event_mask,
							    notify_type,
							    hnotification);
	}
	/* ensure we return a documented return code */
730
	DBC_ENSURE(!status || status == -EFAULT ||
731 732 733 734 735 736 737 738 739 740
		   status == -ETIME || status == -ESRCH ||
		   status == -ENOSYS || status == -EPERM);
	return status;
}

/*
 *  ======== strm_select ========
 *  Purpose:
 *      Selects a ready stream.
 */
741
int strm_select(struct strm_object **strm_tab, u32 strms,
742
		       u32 *pmask, u32 utimeout)
743 744 745 746 747 748 749 750 751 752 753
{
	u32 index;
	struct chnl_info chnl_info_obj;
	struct bridge_drv_interface *intf_fxns;
	struct sync_object **sync_events = NULL;
	u32 i;
	int status = 0;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(strm_tab != NULL);
	DBC_REQUIRE(pmask != NULL);
754
	DBC_REQUIRE(strms > 0);
755 756

	*pmask = 0;
757
	for (i = 0; i < strms; i++) {
758 759 760 761 762
		if (!strm_tab[i]) {
			status = -EFAULT;
			break;
		}
	}
763
	if (status)
764 765 766
		goto func_end;

	/* Determine which channels have IO ready */
767
	for (i = 0; i < strms; i++) {
768 769 770
		intf_fxns = strm_tab[i]->strm_mgr_obj->intf_fxns;
		status = (*intf_fxns->pfn_chnl_get_info) (strm_tab[i]->chnl_obj,
							  &chnl_info_obj);
771
		if (status) {
772 773 774 775 776 777 778
			break;
		} else {
			if (chnl_info_obj.cio_cs > 0)
				*pmask |= (1 << i);

		}
	}
779
	if (!status && utimeout > 0 && *pmask == 0) {
780
		/* Non-zero timeout */
781
		sync_events = kmalloc(strms * sizeof(struct sync_object *),
782 783 784 785 786
								GFP_KERNEL);

		if (sync_events == NULL) {
			status = -ENOMEM;
		} else {
787
			for (i = 0; i < strms; i++) {
788 789 790 791
				intf_fxns =
				    strm_tab[i]->strm_mgr_obj->intf_fxns;
				status = (*intf_fxns->pfn_chnl_get_info)
				    (strm_tab[i]->chnl_obj, &chnl_info_obj);
792
				if (status)
793 794 795 796 797 798 799
					break;
				else
					sync_events[i] =
					    chnl_info_obj.sync_event;

			}
		}
800
		if (!status) {
801
			status =
802
			    sync_wait_on_multiple_events(sync_events, strms,
803
							 utimeout, &index);
804
			if (!status) {
805 806 807 808 809 810 811 812 813 814
				/* Since we waited on the event, we have to
				 * reset it */
				sync_set_event(sync_events[index]);
				*pmask = 1 << index;
			}
		}
	}
func_end:
	kfree(sync_events);

815
	DBC_ENSURE((!status && (*pmask != 0 || utimeout == 0)) ||
816
		   (status && *pmask == 0));
817 818 819 820 821 822 823 824 825

	return status;
}

/*
 *  ======== delete_strm ========
 *  Purpose:
 *      Frees the resources allocated for a stream.
 */
826
static int delete_strm(struct strm_object *stream_obj)
827 828 829 830
{
	struct bridge_drv_interface *intf_fxns;
	int status = 0;

831 832 833
	if (stream_obj) {
		if (stream_obj->chnl_obj) {
			intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
834 835
			/* Channel close can fail only if the channel handle
			 * is invalid. */
836 837
			status = (*intf_fxns->pfn_chnl_close)
					(stream_obj->chnl_obj);
838
			/* Free all SM address translator resources */
839
			if (!status) {
840
				if (stream_obj->xlator) {
841
					/* force free */
842 843
					(void)cmm_xlator_delete(stream_obj->
								xlator,
844 845 846 847
								true);
				}
			}
		}
848
		kfree(stream_obj);
849 850 851 852 853
	} else {
		status = -EFAULT;
	}
	return status;
}