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