PageRenderTime 616ms CodeModel.GetById 8ms app.highlight 550ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/staging/crystalhd/crystalhd_misc.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 1032 lines | 623 code | 116 blank | 293 comment | 147 complexity | c6a65cd2c5852e043cd34ad40e267611 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
   1/***************************************************************************
   2 *	   Copyright (c) 2005-2009, Broadcom Corporation.
   3 *
   4 *  Name: crystalhd_misc . c
   5 *
   6 *  Description:
   7 *		BCM70012 Linux driver misc routines.
   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 <linux/slab.h>
  28
  29#include "crystalhd_misc.h"
  30#include "crystalhd_lnx.h"
  31
  32uint32_t g_linklog_level;
  33
  34static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
  35{
  36	crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
  37	return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
  38}
  39
  40static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
  41{
  42	crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
  43	bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
  44}
  45
  46static inline enum BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
  47{
  48	return BC_STS_SUCCESS;
  49}
  50
  51static struct crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
  52{
  53	unsigned long flags = 0;
  54	struct crystalhd_dio_req *temp = NULL;
  55
  56	if (!adp) {
  57		BCMLOG_ERR("Invalid Arg!!\n");
  58		return temp;
  59	}
  60
  61	spin_lock_irqsave(&adp->lock, flags);
  62	temp = adp->ua_map_free_head;
  63	if (temp)
  64		adp->ua_map_free_head = adp->ua_map_free_head->next;
  65	spin_unlock_irqrestore(&adp->lock, flags);
  66
  67	return temp;
  68}
  69
  70static void crystalhd_free_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
  71{
  72	unsigned long flags = 0;
  73
  74	if (!adp || !dio)
  75		return;
  76	spin_lock_irqsave(&adp->lock, flags);
  77	dio->sig = crystalhd_dio_inv;
  78	dio->page_cnt = 0;
  79	dio->fb_size = 0;
  80	memset(&dio->uinfo, 0, sizeof(dio->uinfo));
  81	dio->next = adp->ua_map_free_head;
  82	adp->ua_map_free_head = dio;
  83	spin_unlock_irqrestore(&adp->lock, flags);
  84}
  85
  86static struct crystalhd_elem *crystalhd_alloc_elem(struct crystalhd_adp *adp)
  87{
  88	unsigned long flags = 0;
  89	struct crystalhd_elem *temp = NULL;
  90
  91	if (!adp)
  92		return temp;
  93	spin_lock_irqsave(&adp->lock, flags);
  94	temp = adp->elem_pool_head;
  95	if (temp) {
  96		adp->elem_pool_head = adp->elem_pool_head->flink;
  97		memset(temp, 0, sizeof(*temp));
  98	}
  99	spin_unlock_irqrestore(&adp->lock, flags);
 100
 101	return temp;
 102}
 103static void crystalhd_free_elem(struct crystalhd_adp *adp, struct crystalhd_elem *elem)
 104{
 105	unsigned long flags = 0;
 106
 107	if (!adp || !elem)
 108		return;
 109	spin_lock_irqsave(&adp->lock, flags);
 110	elem->flink = adp->elem_pool_head;
 111	adp->elem_pool_head = elem;
 112	spin_unlock_irqrestore(&adp->lock, flags);
 113}
 114
 115static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
 116				  unsigned int len, unsigned int offset)
 117{
 118	sg_set_page(sg, page, len, offset);
 119#ifdef CONFIG_X86_64
 120	sg->dma_length = len;
 121#endif
 122}
 123
 124static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
 125{
 126	/* http://lkml.org/lkml/2007/11/27/68 */
 127	sg_init_table(sg, entries);
 128}
 129
 130/*========================== Extern ========================================*/
 131/**
 132 * bc_dec_reg_rd - Read 7412's device register.
 133 * @adp: Adapter instance
 134 * @reg_off: Register offset.
 135 *
 136 * Return:
 137 *	32bit value read
 138 *
 139 * 7412's device register read routine. This interface use
 140 * 7412's device access range mapped from BAR-2 (4M) of PCIe
 141 * configuration space.
 142 */
 143uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
 144{
 145	if (!adp || (reg_off > adp->pci_mem_len)) {
 146		BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
 147		return 0;
 148	}
 149
 150	return readl(adp->addr + reg_off);
 151}
 152
 153/**
 154 * bc_dec_reg_wr - Write 7412's device register
 155 * @adp: Adapter instance
 156 * @reg_off: Register offset.
 157 * @val: Dword value to be written.
 158 *
 159 * Return:
 160 *	none.
 161 *
 162 * 7412's device register write routine. This interface use
 163 * 7412's device access range mapped from BAR-2 (4M) of PCIe
 164 * configuration space.
 165 */
 166void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
 167{
 168	if (!adp || (reg_off > adp->pci_mem_len)) {
 169		BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
 170		return;
 171	}
 172	writel(val, adp->addr + reg_off);
 173	udelay(8);
 174}
 175
 176/**
 177 * crystalhd_reg_rd - Read Link's device register.
 178 * @adp: Adapter instance
 179 * @reg_off: Register offset.
 180 *
 181 * Return:
 182 *	32bit value read
 183 *
 184 * Link device register  read routine. This interface use
 185 * Link's device access range mapped from BAR-1 (64K) of PCIe
 186 * configuration space.
 187 *
 188 */
 189uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
 190{
 191	if (!adp || (reg_off > adp->pci_i2o_len)) {
 192		BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
 193		return 0;
 194	}
 195	return readl(adp->i2o_addr + reg_off);
 196}
 197
 198/**
 199 * crystalhd_reg_wr - Write Link's device register
 200 * @adp: Adapter instance
 201 * @reg_off: Register offset.
 202 * @val: Dword value to be written.
 203 *
 204 * Return:
 205 *	none.
 206 *
 207 * Link device register  write routine. This interface use
 208 * Link's device access range mapped from BAR-1 (64K) of PCIe
 209 * configuration space.
 210 *
 211 */
 212void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
 213{
 214	if (!adp || (reg_off > adp->pci_i2o_len)) {
 215		BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
 216		return;
 217	}
 218	writel(val, adp->i2o_addr + reg_off);
 219}
 220
 221/**
 222 * crystalhd_mem_rd - Read data from 7412's DRAM area.
 223 * @adp: Adapter instance
 224 * @start_off: Start offset.
 225 * @dw_cnt: Count in dwords.
 226 * @rd_buff: Buffer to copy the data from dram.
 227 *
 228 * Return:
 229 *	Status.
 230 *
 231 * 7412's Dram read routine.
 232 */
 233enum BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
 234			 uint32_t dw_cnt, uint32_t *rd_buff)
 235{
 236	uint32_t ix = 0;
 237
 238	if (!adp || !rd_buff ||
 239	    (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
 240		BCMLOG_ERR("Invalid arg\n");
 241		return BC_STS_INV_ARG;
 242	}
 243	for (ix = 0; ix < dw_cnt; ix++)
 244		rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
 245
 246	return BC_STS_SUCCESS;
 247}
 248
 249/**
 250 * crystalhd_mem_wr - Write data to 7412's DRAM area.
 251 * @adp: Adapter instance
 252 * @start_off: Start offset.
 253 * @dw_cnt: Count in dwords.
 254 * @wr_buff: Data Buffer to be written.
 255 *
 256 * Return:
 257 *	Status.
 258 *
 259 * 7412's Dram write routine.
 260 */
 261enum BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
 262			 uint32_t dw_cnt, uint32_t *wr_buff)
 263{
 264	uint32_t ix = 0;
 265
 266	if (!adp || !wr_buff ||
 267	    (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
 268		BCMLOG_ERR("Invalid arg\n");
 269		return BC_STS_INV_ARG;
 270	}
 271
 272	for (ix = 0; ix < dw_cnt; ix++)
 273		crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
 274
 275	return BC_STS_SUCCESS;
 276}
 277/**
 278 * crystalhd_pci_cfg_rd - PCIe config read
 279 * @adp: Adapter instance
 280 * @off: PCI config space offset.
 281 * @len: Size -- Byte, Word & dword.
 282 * @val: Value read
 283 *
 284 * Return:
 285 *	Status.
 286 *
 287 * Get value from Link's PCIe config space.
 288 */
 289enum BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
 290			     uint32_t len, uint32_t *val)
 291{
 292	enum BC_STATUS sts = BC_STS_SUCCESS;
 293	int rc = 0;
 294
 295	if (!adp || !val) {
 296		BCMLOG_ERR("Invalid arg\n");
 297		return BC_STS_INV_ARG;
 298	}
 299
 300	switch (len) {
 301	case 1:
 302		rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
 303		break;
 304	case 2:
 305		rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
 306		break;
 307	case 4:
 308		rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
 309		break;
 310	default:
 311		rc = -EINVAL;
 312		sts = BC_STS_INV_ARG;
 313		BCMLOG_ERR("Invalid len:%d\n", len);
 314	}
 315
 316	if (rc && (sts == BC_STS_SUCCESS))
 317		sts = BC_STS_ERROR;
 318
 319	return sts;
 320}
 321
 322/**
 323 * crystalhd_pci_cfg_wr - PCIe config write
 324 * @adp: Adapter instance
 325 * @off: PCI config space offset.
 326 * @len: Size -- Byte, Word & dword.
 327 * @val: Value to be written
 328 *
 329 * Return:
 330 *	Status.
 331 *
 332 * Set value to Link's PCIe config space.
 333 */
 334enum BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
 335			     uint32_t len, uint32_t val)
 336{
 337	enum BC_STATUS sts = BC_STS_SUCCESS;
 338	int rc = 0;
 339
 340	if (!adp || !val) {
 341		BCMLOG_ERR("Invalid arg\n");
 342		return BC_STS_INV_ARG;
 343	}
 344
 345	switch (len) {
 346	case 1:
 347		rc = pci_write_config_byte(adp->pdev, off, (u8)val);
 348		break;
 349	case 2:
 350		rc = pci_write_config_word(adp->pdev, off, (u16)val);
 351		break;
 352	case 4:
 353		rc = pci_write_config_dword(adp->pdev, off, val);
 354		break;
 355	default:
 356		rc = -EINVAL;
 357		sts = BC_STS_INV_ARG;
 358		BCMLOG_ERR("Invalid len:%d\n", len);
 359	}
 360
 361	if (rc && (sts == BC_STS_SUCCESS))
 362		sts = BC_STS_ERROR;
 363
 364	return sts;
 365}
 366
 367/**
 368 * bc_kern_dma_alloc - Allocate memory for Dma rings
 369 * @adp: Adapter instance
 370 * @sz: Size of the memory to allocate.
 371 * @phy_addr: Physical address of the memory allocated.
 372 *	   Typedef to system's dma_addr_t (u64)
 373 *
 374 * Return:
 375 *  Pointer to allocated memory..
 376 *
 377 * Wrapper to Linux kernel interface.
 378 *
 379 */
 380void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
 381			dma_addr_t *phy_addr)
 382{
 383	void *temp = NULL;
 384
 385	if (!adp || !sz || !phy_addr) {
 386		BCMLOG_ERR("Invalide Arg..\n");
 387		return temp;
 388	}
 389
 390	temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
 391	if (temp)
 392		memset(temp, 0, sz);
 393
 394	return temp;
 395}
 396
 397/**
 398 * bc_kern_dma_free - Release Dma ring memory.
 399 * @adp: Adapter instance
 400 * @sz: Size of the memory to allocate.
 401 * @ka: Kernel virtual address returned during _dio_alloc()
 402 * @phy_addr: Physical address of the memory allocated.
 403 *	   Typedef to system's dma_addr_t (u64)
 404 *
 405 * Return:
 406 *     none.
 407 */
 408void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
 409		      dma_addr_t phy_addr)
 410{
 411	if (!adp || !ka || !sz || !phy_addr) {
 412		BCMLOG_ERR("Invalide Arg..\n");
 413		return;
 414	}
 415
 416	pci_free_consistent(adp->pdev, sz, ka, phy_addr);
 417}
 418
 419/**
 420 * crystalhd_create_dioq - Create Generic DIO queue
 421 * @adp: Adapter instance
 422 * @dioq_hnd: Handle to the dio queue created
 423 * @cb	: Optional - Call back To free the element.
 424 * @cbctx: Context to pass to callback.
 425 *
 426 * Return:
 427 *  status
 428 *
 429 * Initialize Generic DIO queue to hold any data. Callback
 430 * will be used to free elements while deleting the queue.
 431 */
 432enum BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
 433			      struct crystalhd_dioq **dioq_hnd,
 434			      crystalhd_data_free_cb cb, void *cbctx)
 435{
 436	struct crystalhd_dioq *dioq = NULL;
 437
 438	if (!adp || !dioq_hnd) {
 439		BCMLOG_ERR("Invalid arg!!\n");
 440		return BC_STS_INV_ARG;
 441	}
 442
 443	dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
 444	if (!dioq)
 445		return BC_STS_INSUFF_RES;
 446
 447	spin_lock_init(&dioq->lock);
 448	dioq->sig = BC_LINK_DIOQ_SIG;
 449	dioq->head = (struct crystalhd_elem *)&dioq->head;
 450	dioq->tail = (struct crystalhd_elem *)&dioq->head;
 451	crystalhd_create_event(&dioq->event);
 452	dioq->adp = adp;
 453	dioq->data_rel_cb = cb;
 454	dioq->cb_context = cbctx;
 455	*dioq_hnd = dioq;
 456
 457	return BC_STS_SUCCESS;
 458}
 459
 460/**
 461 * crystalhd_delete_dioq - Delete Generic DIO queue
 462 * @adp: Adapter instance
 463 * @dioq: DIOQ instance..
 464 *
 465 * Return:
 466 *  None.
 467 *
 468 * Release Generic DIO queue. This function will remove
 469 * all the entries from the Queue and will release data
 470 * by calling the call back provided during creation.
 471 *
 472 */
 473void crystalhd_delete_dioq(struct crystalhd_adp *adp, struct crystalhd_dioq *dioq)
 474{
 475	void *temp;
 476
 477	if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
 478		return;
 479
 480	do {
 481		temp = crystalhd_dioq_fetch(dioq);
 482		if (temp && dioq->data_rel_cb)
 483			dioq->data_rel_cb(dioq->cb_context, temp);
 484	} while (temp);
 485	dioq->sig = 0;
 486	kfree(dioq);
 487}
 488
 489/**
 490 * crystalhd_dioq_add - Add new DIO request element.
 491 * @ioq: DIO queue instance
 492 * @t: DIO request to be added.
 493 * @wake: True - Wake up suspended process.
 494 * @tag: Special tag to assign - For search and get.
 495 *
 496 * Return:
 497 *  Status.
 498 *
 499 * Insert new element to Q tail.
 500 */
 501enum BC_STATUS crystalhd_dioq_add(struct crystalhd_dioq *ioq, void *data,
 502			   bool wake, uint32_t tag)
 503{
 504	unsigned long flags = 0;
 505	struct crystalhd_elem *tmp;
 506
 507	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
 508		BCMLOG_ERR("Invalid arg!!\n");
 509		return BC_STS_INV_ARG;
 510	}
 511
 512	tmp = crystalhd_alloc_elem(ioq->adp);
 513	if (!tmp) {
 514		BCMLOG_ERR("No free elements.\n");
 515		return BC_STS_INSUFF_RES;
 516	}
 517
 518	tmp->data = data;
 519	tmp->tag = tag;
 520	spin_lock_irqsave(&ioq->lock, flags);
 521	tmp->flink = (struct crystalhd_elem *)&ioq->head;
 522	tmp->blink = ioq->tail;
 523	tmp->flink->blink = tmp;
 524	tmp->blink->flink = tmp;
 525	ioq->count++;
 526	spin_unlock_irqrestore(&ioq->lock, flags);
 527
 528	if (wake)
 529		crystalhd_set_event(&ioq->event);
 530
 531	return BC_STS_SUCCESS;
 532}
 533
 534/**
 535 * crystalhd_dioq_fetch - Fetch element from head.
 536 * @ioq: DIO queue instance
 537 *
 538 * Return:
 539 *	data element from the head..
 540 *
 541 * Remove an element from Queue.
 542 */
 543void *crystalhd_dioq_fetch(struct crystalhd_dioq *ioq)
 544{
 545	unsigned long flags = 0;
 546	struct crystalhd_elem *tmp;
 547	struct crystalhd_elem *ret = NULL;
 548	void *data = NULL;
 549
 550	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
 551		BCMLOG_ERR("Invalid arg!!\n");
 552		return data;
 553	}
 554
 555	spin_lock_irqsave(&ioq->lock, flags);
 556	tmp = ioq->head;
 557	if (tmp != (struct crystalhd_elem *)&ioq->head) {
 558		ret = tmp;
 559		tmp->flink->blink = tmp->blink;
 560		tmp->blink->flink = tmp->flink;
 561		ioq->count--;
 562	}
 563	spin_unlock_irqrestore(&ioq->lock, flags);
 564	if (ret) {
 565		data = ret->data;
 566		crystalhd_free_elem(ioq->adp, ret);
 567	}
 568
 569	return data;
 570}
 571/**
 572 * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
 573 * @ioq: DIO queue instance
 574 * @tag: Tag to search for.
 575 *
 576 * Return:
 577 *	element from the head..
 578 *
 579 * Search TAG and remove the element.
 580 */
 581void *crystalhd_dioq_find_and_fetch(struct crystalhd_dioq *ioq, uint32_t tag)
 582{
 583	unsigned long flags = 0;
 584	struct crystalhd_elem *tmp;
 585	struct crystalhd_elem *ret = NULL;
 586	void *data = NULL;
 587
 588	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
 589		BCMLOG_ERR("Invalid arg!!\n");
 590		return data;
 591	}
 592
 593	spin_lock_irqsave(&ioq->lock, flags);
 594	tmp = ioq->head;
 595	while (tmp != (struct crystalhd_elem *)&ioq->head) {
 596		if (tmp->tag == tag) {
 597			ret = tmp;
 598			tmp->flink->blink = tmp->blink;
 599			tmp->blink->flink = tmp->flink;
 600			ioq->count--;
 601			break;
 602		}
 603		tmp = tmp->flink;
 604	}
 605	spin_unlock_irqrestore(&ioq->lock, flags);
 606
 607	if (ret) {
 608		data = ret->data;
 609		crystalhd_free_elem(ioq->adp, ret);
 610	}
 611
 612	return data;
 613}
 614
 615/**
 616 * crystalhd_dioq_fetch_wait - Fetch element from Head.
 617 * @ioq: DIO queue instance
 618 * @to_secs: Wait timeout in seconds..
 619 *
 620 * Return:
 621 *	element from the head..
 622 *
 623 * Return element from head if Q is not empty. Wait for new element
 624 * if Q is empty for Timeout seconds.
 625 */
 626void *crystalhd_dioq_fetch_wait(struct crystalhd_dioq *ioq, uint32_t to_secs,
 627			      uint32_t *sig_pend)
 628{
 629	unsigned long flags = 0;
 630	int rc = 0, count;
 631	void *tmp = NULL;
 632
 633	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
 634		BCMLOG_ERR("Invalid arg!!\n");
 635		return tmp;
 636	}
 637
 638	count = to_secs;
 639	spin_lock_irqsave(&ioq->lock, flags);
 640	while ((ioq->count == 0) && count) {
 641		spin_unlock_irqrestore(&ioq->lock, flags);
 642
 643		crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
 644		if (rc == 0) {
 645			goto out;
 646		} else if (rc == -EINTR) {
 647			BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
 648			*sig_pend = 1;
 649			return tmp;
 650		}
 651		spin_lock_irqsave(&ioq->lock, flags);
 652		count--;
 653	}
 654	spin_unlock_irqrestore(&ioq->lock, flags);
 655
 656out:
 657	return crystalhd_dioq_fetch(ioq);
 658}
 659
 660/**
 661 * crystalhd_map_dio - Map user address for DMA
 662 * @adp:	Adapter instance
 663 * @ubuff:	User buffer to map.
 664 * @ubuff_sz:	User buffer size.
 665 * @uv_offset:	UV buffer offset.
 666 * @en_422mode: TRUE:422 FALSE:420 Capture mode.
 667 * @dir_tx:	TRUE for Tx (To device from host)
 668 * @dio_hnd:	Handle to mapped DIO request.
 669 *
 670 * Return:
 671 *	Status.
 672 *
 673 * This routine maps user address and lock pages for DMA.
 674 *
 675 */
 676enum BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
 677			  uint32_t ubuff_sz, uint32_t uv_offset,
 678			  bool en_422mode, bool dir_tx,
 679			  struct crystalhd_dio_req **dio_hnd)
 680{
 681	struct crystalhd_dio_req	*dio;
 682	/* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
 683	unsigned long start = 0, end = 0, uaddr = 0, count = 0;
 684	unsigned long spsz = 0, uv_start = 0;
 685	int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
 686
 687	if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
 688		BCMLOG_ERR("Invalid arg\n");
 689		return BC_STS_INV_ARG;
 690	}
 691	/* Compute pages */
 692	uaddr = (unsigned long)ubuff;
 693	count = (unsigned long)ubuff_sz;
 694	end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
 695	start = uaddr >> PAGE_SHIFT;
 696	nr_pages = end - start;
 697
 698	if (!count || ((uaddr + count) < uaddr)) {
 699		BCMLOG_ERR("User addr overflow!!\n");
 700		return BC_STS_INV_ARG;
 701	}
 702
 703	dio = crystalhd_alloc_dio(adp);
 704	if (!dio) {
 705		BCMLOG_ERR("dio pool empty..\n");
 706		return BC_STS_INSUFF_RES;
 707	}
 708
 709	if (dir_tx) {
 710		rw = WRITE;
 711		dio->direction = DMA_TO_DEVICE;
 712	} else {
 713		rw = READ;
 714		dio->direction = DMA_FROM_DEVICE;
 715	}
 716
 717	if (nr_pages > dio->max_pages) {
 718		BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
 719			   dio->max_pages, nr_pages);
 720		crystalhd_unmap_dio(adp, dio);
 721		return BC_STS_INSUFF_RES;
 722	}
 723
 724	if (uv_offset) {
 725		uv_start = (uaddr + (unsigned long)uv_offset)  >> PAGE_SHIFT;
 726		dio->uinfo.uv_sg_ix = uv_start - start;
 727		dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
 728	}
 729
 730	dio->fb_size = ubuff_sz & 0x03;
 731	if (dio->fb_size) {
 732		res = copy_from_user(dio->fb_va,
 733				     (void *)(uaddr + count - dio->fb_size),
 734				     dio->fb_size);
 735		if (res) {
 736			BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
 737				   res, dio->fb_size,
 738				   (void *)(uaddr + count-dio->fb_size));
 739			crystalhd_unmap_dio(adp, dio);
 740			return BC_STS_INSUFF_RES;
 741		}
 742	}
 743
 744	down_read(&current->mm->mmap_sem);
 745	res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
 746			     0, dio->pages, NULL);
 747	up_read(&current->mm->mmap_sem);
 748
 749	/* Save for release..*/
 750	dio->sig = crystalhd_dio_locked;
 751	if (res < nr_pages) {
 752		BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
 753		dio->page_cnt = res;
 754		crystalhd_unmap_dio(adp, dio);
 755		return BC_STS_ERROR;
 756	}
 757
 758	dio->page_cnt = nr_pages;
 759	/* Get scatter/gather */
 760	crystalhd_init_sg(dio->sg, dio->page_cnt);
 761	crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
 762	if (nr_pages > 1) {
 763		dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
 764
 765#ifdef CONFIG_X86_64
 766		dio->sg[0].dma_length = dio->sg[0].length;
 767#endif
 768		count -= dio->sg[0].length;
 769		for (i = 1; i < nr_pages; i++) {
 770			if (count < 4) {
 771				spsz = count;
 772				skip_fb_sg = 1;
 773			} else {
 774				spsz = (count < PAGE_SIZE) ?
 775					(count & ~0x03) : PAGE_SIZE;
 776			}
 777			crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
 778			count -= spsz;
 779		}
 780	} else {
 781		if (count < 4) {
 782			dio->sg[0].length = count;
 783			skip_fb_sg = 1;
 784		} else {
 785			dio->sg[0].length = count - dio->fb_size;
 786		}
 787#ifdef CONFIG_X86_64
 788		dio->sg[0].dma_length = dio->sg[0].length;
 789#endif
 790	}
 791	dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
 792				 dio->page_cnt, dio->direction);
 793	if (dio->sg_cnt <= 0) {
 794		BCMLOG_ERR("sg map %d-%d\n", dio->sg_cnt, dio->page_cnt);
 795		crystalhd_unmap_dio(adp, dio);
 796		return BC_STS_ERROR;
 797	}
 798	if (dio->sg_cnt && skip_fb_sg)
 799		dio->sg_cnt -= 1;
 800	dio->sig = crystalhd_dio_sg_mapped;
 801	/* Fill in User info.. */
 802	dio->uinfo.xfr_len   = ubuff_sz;
 803	dio->uinfo.xfr_buff  = ubuff;
 804	dio->uinfo.uv_offset = uv_offset;
 805	dio->uinfo.b422mode  = en_422mode;
 806	dio->uinfo.dir_tx    = dir_tx;
 807
 808	*dio_hnd = dio;
 809
 810	return BC_STS_SUCCESS;
 811}
 812
 813/**
 814 * crystalhd_unmap_sgl - Release mapped resources
 815 * @adp: Adapter instance
 816 * @dio: DIO request instance
 817 *
 818 * Return:
 819 *	Status.
 820 *
 821 * This routine is to unmap the user buffer pages.
 822 */
 823enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
 824{
 825	struct page *page = NULL;
 826	int j = 0;
 827
 828	if (!adp || !dio) {
 829		BCMLOG_ERR("Invalid arg\n");
 830		return BC_STS_INV_ARG;
 831	}
 832
 833	if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
 834		for (j = 0; j < dio->page_cnt; j++) {
 835			page = dio->pages[j];
 836			if (page) {
 837				if (!PageReserved(page) &&
 838				    (dio->direction == DMA_FROM_DEVICE))
 839					SetPageDirty(page);
 840				page_cache_release(page);
 841			}
 842		}
 843	}
 844	if (dio->sig == crystalhd_dio_sg_mapped)
 845		pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
 846
 847	crystalhd_free_dio(adp, dio);
 848
 849	return BC_STS_SUCCESS;
 850}
 851
 852/**
 853 * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
 854 * @adp: Adapter instance
 855 * @max_pages: Max pages for size calculation.
 856 *
 857 * Return:
 858 *	system error.
 859 *
 860 * This routine creates a memory pool to hold dio context for
 861 * for HW Direct IO operation.
 862 */
 863int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
 864{
 865	uint32_t asz = 0, i = 0;
 866	uint8_t	*temp;
 867	struct crystalhd_dio_req *dio;
 868
 869	if (!adp || !max_pages) {
 870		BCMLOG_ERR("Invalid Arg!!\n");
 871		return -EINVAL;
 872	}
 873
 874	/* Get dma memory for fill byte handling..*/
 875	adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
 876					      adp->pdev, 8, 8, 0);
 877	if (!adp->fill_byte_pool) {
 878		BCMLOG_ERR("failed to create fill byte pool\n");
 879		return -ENOMEM;
 880	}
 881
 882	/* Get the max size from user based on 420/422 modes */
 883	asz =  (sizeof(*dio->pages) * max_pages) +
 884	       (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
 885
 886	BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
 887	       BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
 888
 889	for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
 890		temp = kzalloc(asz, GFP_KERNEL);
 891		if ((temp) == NULL) {
 892			BCMLOG_ERR("Failed to alloc %d mem\n", asz);
 893			return -ENOMEM;
 894		}
 895
 896		dio = (struct crystalhd_dio_req *)temp;
 897		temp += sizeof(*dio);
 898		dio->pages = (struct page **)temp;
 899		temp += (sizeof(*dio->pages) * max_pages);
 900		dio->sg = (struct scatterlist *)temp;
 901		dio->max_pages = max_pages;
 902		dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
 903					    &dio->fb_pa);
 904		if (!dio->fb_va) {
 905			BCMLOG_ERR("fill byte alloc failed.\n");
 906			return -ENOMEM;
 907		}
 908
 909		crystalhd_free_dio(adp, dio);
 910	}
 911
 912	return 0;
 913}
 914
 915/**
 916 * crystalhd_destroy_dio_pool - Release DIO mem pool.
 917 * @adp: Adapter instance
 918 *
 919 * Return:
 920 *	none.
 921 *
 922 * This routine releases dio memory pool during close.
 923 */
 924void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
 925{
 926	struct crystalhd_dio_req *dio;
 927	int count = 0;
 928
 929	if (!adp) {
 930		BCMLOG_ERR("Invalid Arg!!\n");
 931		return;
 932	}
 933
 934	do {
 935		dio = crystalhd_alloc_dio(adp);
 936		if (dio) {
 937			if (dio->fb_va)
 938				pci_pool_free(adp->fill_byte_pool,
 939					      dio->fb_va, dio->fb_pa);
 940			count++;
 941			kfree(dio);
 942		}
 943	} while (dio);
 944
 945	if (adp->fill_byte_pool) {
 946		pci_pool_destroy(adp->fill_byte_pool);
 947		adp->fill_byte_pool = NULL;
 948	}
 949
 950	BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count);
 951}
 952
 953/**
 954 * crystalhd_create_elem_pool - List element pool creation.
 955 * @adp: Adapter instance
 956 * @pool_size: Number of elements in the pool.
 957 *
 958 * Return:
 959 *	0 - success, <0 error
 960 *
 961 * Create general purpose list element pool to hold pending,
 962 * and active requests.
 963 */
 964int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
 965		uint32_t pool_size)
 966{
 967	uint32_t i;
 968	struct crystalhd_elem *temp;
 969
 970	if (!adp || !pool_size)
 971		return -EINVAL;
 972
 973	for (i = 0; i < pool_size; i++) {
 974		temp = kzalloc(sizeof(*temp), GFP_KERNEL);
 975		if (!temp) {
 976			BCMLOG_ERR("kalloc failed\n");
 977			return -ENOMEM;
 978		}
 979		crystalhd_free_elem(adp, temp);
 980	}
 981	BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
 982	return 0;
 983}
 984
 985/**
 986 * crystalhd_delete_elem_pool - List element pool deletion.
 987 * @adp: Adapter instance
 988 *
 989 * Return:
 990 *	none
 991 *
 992 * Delete general purpose list element pool.
 993 */
 994void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
 995{
 996	struct crystalhd_elem *temp;
 997	int dbg_cnt = 0;
 998
 999	if (!adp)
1000		return;
1001
1002	do {
1003		temp = crystalhd_alloc_elem(adp);
1004		if (temp) {
1005			kfree(temp);
1006			dbg_cnt++;
1007		}
1008	} while (temp);
1009
1010	BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
1011}
1012
1013/*================ Debug support routines.. ================================*/
1014void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
1015{
1016	uint32_t i, k = 1;
1017
1018	for (i = 0; i < dwcount; i++) {
1019		if (k == 1)
1020			BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
1021
1022		BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
1023
1024		buff += sizeof(uint32_t);
1025		off  += sizeof(uint32_t);
1026		k++;
1027		if ((i == dwcount - 1) || (k > 4)) {
1028			BCMLOG(BCMLOG_DATA, "\n");
1029			k = 1;
1030		}
1031	}
1032}