aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/crystalhd/crystalhd_misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/crystalhd/crystalhd_misc.c')
-rw-r--r--drivers/staging/crystalhd/crystalhd_misc.c1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/drivers/staging/crystalhd/crystalhd_misc.c b/drivers/staging/crystalhd/crystalhd_misc.c
new file mode 100644
index 000000000000..587dcc477865
--- /dev/null
+++ b/drivers/staging/crystalhd/crystalhd_misc.c
@@ -0,0 +1,1030 @@
+/***************************************************************************
+ * Copyright (c) 2005-2009, Broadcom Corporation.
+ *
+ * Name: crystalhd_misc . c
+ *
+ * Description:
+ * BCM70012 Linux driver misc routines.
+ *
+ * HISTORY:
+ *
+ **********************************************************************
+ * This file is part of the crystalhd device driver.
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this driver. If not, see <http://www.gnu.org/licenses/>.
+ **********************************************************************/
+
+#include "crystalhd_misc.h"
+#include "crystalhd_lnx.h"
+
+uint32_t g_linklog_level;
+
+static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
+{
+ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
+ return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
+}
+
+static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
+{
+ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
+ bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
+}
+
+static inline BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
+{
+ return BC_STS_SUCCESS;
+}
+
+static crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
+{
+ unsigned long flags = 0;
+ crystalhd_dio_req *temp = NULL;
+
+ if (!adp) {
+ BCMLOG_ERR("Invalid Arg!!\n");
+ return temp;
+ }
+
+ spin_lock_irqsave(&adp->lock, flags);
+ temp = adp->ua_map_free_head;
+ if (temp)
+ adp->ua_map_free_head = adp->ua_map_free_head->next;
+ spin_unlock_irqrestore(&adp->lock, flags);
+
+ return temp;
+}
+
+static void crystalhd_free_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
+{
+ unsigned long flags = 0;
+
+ if (!adp || !dio)
+ return;
+ spin_lock_irqsave(&adp->lock, flags);
+ dio->sig = crystalhd_dio_inv;
+ dio->page_cnt = 0;
+ dio->fb_size = 0;
+ memset(&dio->uinfo, 0, sizeof(dio->uinfo));
+ dio->next = adp->ua_map_free_head;
+ adp->ua_map_free_head = dio;
+ spin_unlock_irqrestore(&adp->lock, flags);
+}
+
+static crystalhd_elem_t *crystalhd_alloc_elem(struct crystalhd_adp *adp)
+{
+ unsigned long flags = 0;
+ crystalhd_elem_t *temp = NULL;
+
+ if (!adp)
+ return temp;
+ spin_lock_irqsave(&adp->lock, flags);
+ temp = adp->elem_pool_head;
+ if (temp) {
+ adp->elem_pool_head = adp->elem_pool_head->flink;
+ memset(temp, 0, sizeof(*temp));
+ }
+ spin_unlock_irqrestore(&adp->lock, flags);
+
+ return temp;
+}
+static void crystalhd_free_elem(struct crystalhd_adp *adp, crystalhd_elem_t *elem)
+{
+ unsigned long flags = 0;
+
+ if (!adp || !elem)
+ return;
+ spin_lock_irqsave(&adp->lock, flags);
+ elem->flink = adp->elem_pool_head;
+ adp->elem_pool_head = elem;
+ spin_unlock_irqrestore(&adp->lock, flags);
+}
+
+static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
+ unsigned int len, unsigned int offset)
+{
+ sg_set_page(sg, page, len, offset);
+#ifdef CONFIG_X86_64
+ sg->dma_length = len;
+#endif
+}
+
+static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
+{
+ /* http://lkml.org/lkml/2007/11/27/68 */
+ sg_init_table(sg, entries);
+}
+
+/*========================== Extern ========================================*/
+/**
+ * bc_dec_reg_rd - Read 7412's device register.
+ * @adp: Adapter instance
+ * @reg_off: Register offset.
+ *
+ * Return:
+ * 32bit value read
+ *
+ * 7412's device register read routine. This interface use
+ * 7412's device access range mapped from BAR-2 (4M) of PCIe
+ * configuration space.
+ */
+uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
+{
+ if (!adp || (reg_off > adp->pci_mem_len)) {
+ BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
+ return 0;
+ }
+
+ return readl(adp->addr + reg_off);
+}
+
+/**
+ * bc_dec_reg_wr - Write 7412's device register
+ * @adp: Adapter instance
+ * @reg_off: Register offset.
+ * @val: Dword value to be written.
+ *
+ * Return:
+ * none.
+ *
+ * 7412's device register write routine. This interface use
+ * 7412's device access range mapped from BAR-2 (4M) of PCIe
+ * configuration space.
+ */
+void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
+{
+ if (!adp || (reg_off > adp->pci_mem_len)) {
+ BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
+ return;
+ }
+ writel(val, adp->addr + reg_off);
+ udelay(8);
+}
+
+/**
+ * crystalhd_reg_rd - Read Link's device register.
+ * @adp: Adapter instance
+ * @reg_off: Register offset.
+ *
+ * Return:
+ * 32bit value read
+ *
+ * Link device register read routine. This interface use
+ * Link's device access range mapped from BAR-1 (64K) of PCIe
+ * configuration space.
+ *
+ */
+uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
+{
+ if (!adp || (reg_off > adp->pci_i2o_len)) {
+ BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
+ return 0;
+ }
+ return readl(adp->i2o_addr + reg_off);
+}
+
+/**
+ * crystalhd_reg_wr - Write Link's device register
+ * @adp: Adapter instance
+ * @reg_off: Register offset.
+ * @val: Dword value to be written.
+ *
+ * Return:
+ * none.
+ *
+ * Link device register write routine. This interface use
+ * Link's device access range mapped from BAR-1 (64K) of PCIe
+ * configuration space.
+ *
+ */
+void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
+{
+ if (!adp || (reg_off > adp->pci_i2o_len)) {
+ BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
+ return;
+ }
+ writel(val, adp->i2o_addr + reg_off);
+}
+
+/**
+ * crystalhd_mem_rd - Read data from 7412's DRAM area.
+ * @adp: Adapter instance
+ * @start_off: Start offset.
+ * @dw_cnt: Count in dwords.
+ * @rd_buff: Buffer to copy the data from dram.
+ *
+ * Return:
+ * Status.
+ *
+ * 7412's Dram read routine.
+ */
+BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
+ uint32_t dw_cnt, uint32_t *rd_buff)
+{
+ uint32_t ix = 0;
+
+ if (!adp || !rd_buff ||
+ (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+ for (ix = 0; ix < dw_cnt; ix++)
+ rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
+
+ return BC_STS_SUCCESS;
+}
+
+/**
+ * crystalhd_mem_wr - Write data to 7412's DRAM area.
+ * @adp: Adapter instance
+ * @start_off: Start offset.
+ * @dw_cnt: Count in dwords.
+ * @wr_buff: Data Buffer to be written.
+ *
+ * Return:
+ * Status.
+ *
+ * 7412's Dram write routine.
+ */
+BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
+ uint32_t dw_cnt, uint32_t *wr_buff)
+{
+ uint32_t ix = 0;
+
+ if (!adp || !wr_buff ||
+ (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+
+ for (ix = 0; ix < dw_cnt; ix++)
+ crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
+
+ return BC_STS_SUCCESS;
+}
+/**
+ * crystalhd_pci_cfg_rd - PCIe config read
+ * @adp: Adapter instance
+ * @off: PCI config space offset.
+ * @len: Size -- Byte, Word & dword.
+ * @val: Value read
+ *
+ * Return:
+ * Status.
+ *
+ * Get value from Link's PCIe config space.
+ */
+BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
+ uint32_t len, uint32_t *val)
+{
+ BC_STATUS sts = BC_STS_SUCCESS;
+ int rc = 0;
+
+ if (!adp || !val) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+
+ switch (len) {
+ case 1:
+ rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
+ break;
+ case 2:
+ rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
+ break;
+ case 4:
+ rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
+ break;
+ default:
+ rc = -EINVAL;
+ sts = BC_STS_INV_ARG;
+ BCMLOG_ERR("Invalid len:%d\n", len);
+ };
+
+ if (rc && (sts == BC_STS_SUCCESS))
+ sts = BC_STS_ERROR;
+
+ return sts;
+}
+
+/**
+ * crystalhd_pci_cfg_wr - PCIe config write
+ * @adp: Adapter instance
+ * @off: PCI config space offset.
+ * @len: Size -- Byte, Word & dword.
+ * @val: Value to be written
+ *
+ * Return:
+ * Status.
+ *
+ * Set value to Link's PCIe config space.
+ */
+BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
+ uint32_t len, uint32_t val)
+{
+ BC_STATUS sts = BC_STS_SUCCESS;
+ int rc = 0;
+
+ if (!adp || !val) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+
+ switch (len) {
+ case 1:
+ rc = pci_write_config_byte(adp->pdev, off, (u8)val);
+ break;
+ case 2:
+ rc = pci_write_config_word(adp->pdev, off, (u16)val);
+ break;
+ case 4:
+ rc = pci_write_config_dword(adp->pdev, off, val);
+ break;
+ default:
+ rc = -EINVAL;
+ sts = BC_STS_INV_ARG;
+ BCMLOG_ERR("Invalid len:%d\n", len);
+ };
+
+ if (rc && (sts == BC_STS_SUCCESS))
+ sts = BC_STS_ERROR;
+
+ return sts;
+}
+
+/**
+ * bc_kern_dma_alloc - Allocate memory for Dma rings
+ * @adp: Adapter instance
+ * @sz: Size of the memory to allocate.
+ * @phy_addr: Physical address of the memory allocated.
+ * Typedef to system's dma_addr_t (u64)
+ *
+ * Return:
+ * Pointer to allocated memory..
+ *
+ * Wrapper to Linux kernel interface.
+ *
+ */
+void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
+ dma_addr_t *phy_addr)
+{
+ void *temp = NULL;
+
+ if (!adp || !sz || !phy_addr) {
+ BCMLOG_ERR("Invalide Arg..\n");
+ return temp;
+ }
+
+ temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
+ if (temp)
+ memset(temp, 0, sz);
+
+ return temp;
+}
+
+/**
+ * bc_kern_dma_free - Release Dma ring memory.
+ * @adp: Adapter instance
+ * @sz: Size of the memory to allocate.
+ * @ka: Kernel virtual address returned during _dio_alloc()
+ * @phy_addr: Physical address of the memory allocated.
+ * Typedef to system's dma_addr_t (u64)
+ *
+ * Return:
+ * none.
+ */
+void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
+ dma_addr_t phy_addr)
+{
+ if (!adp || !ka || !sz || !phy_addr) {
+ BCMLOG_ERR("Invalide Arg..\n");
+ return;
+ }
+
+ pci_free_consistent(adp->pdev, sz, ka, phy_addr);
+}
+
+/**
+ * crystalhd_create_dioq - Create Generic DIO queue
+ * @adp: Adapter instance
+ * @dioq_hnd: Handle to the dio queue created
+ * @cb : Optional - Call back To free the element.
+ * @cbctx: Context to pass to callback.
+ *
+ * Return:
+ * status
+ *
+ * Initialize Generic DIO queue to hold any data. Callback
+ * will be used to free elements while deleting the queue.
+ */
+BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
+ crystalhd_dioq_t **dioq_hnd,
+ crystalhd_data_free_cb cb, void *cbctx)
+{
+ crystalhd_dioq_t *dioq = NULL;
+
+ if (!adp || !dioq_hnd) {
+ BCMLOG_ERR("Invalid arg!!\n");
+ return BC_STS_INV_ARG;
+ }
+
+ dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
+ if (!dioq)
+ return BC_STS_INSUFF_RES;
+
+ spin_lock_init(&dioq->lock);
+ dioq->sig = BC_LINK_DIOQ_SIG;
+ dioq->head = (crystalhd_elem_t *)&dioq->head;
+ dioq->tail = (crystalhd_elem_t *)&dioq->head;
+ crystalhd_create_event(&dioq->event);
+ dioq->adp = adp;
+ dioq->data_rel_cb = cb;
+ dioq->cb_context = cbctx;
+ *dioq_hnd = dioq;
+
+ return BC_STS_SUCCESS;
+}
+
+/**
+ * crystalhd_delete_dioq - Delete Generic DIO queue
+ * @adp: Adapter instance
+ * @dioq: DIOQ instance..
+ *
+ * Return:
+ * None.
+ *
+ * Release Generic DIO queue. This function will remove
+ * all the entries from the Queue and will release data
+ * by calling the call back provided during creation.
+ *
+ */
+void crystalhd_delete_dioq(struct crystalhd_adp *adp, crystalhd_dioq_t *dioq)
+{
+ void *temp;
+
+ if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
+ return;
+
+ do {
+ temp = crystalhd_dioq_fetch(dioq);
+ if (temp && dioq->data_rel_cb)
+ dioq->data_rel_cb(dioq->cb_context, temp);
+ } while (temp);
+ dioq->sig = 0;
+ kfree(dioq);
+}
+
+/**
+ * crystalhd_dioq_add - Add new DIO request element.
+ * @ioq: DIO queue instance
+ * @t: DIO request to be added.
+ * @wake: True - Wake up suspended process.
+ * @tag: Special tag to assign - For search and get.
+ *
+ * Return:
+ * Status.
+ *
+ * Insert new element to Q tail.
+ */
+BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data,
+ bool wake, uint32_t tag)
+{
+ unsigned long flags = 0;
+ crystalhd_elem_t *tmp;
+
+ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
+ BCMLOG_ERR("Invalid arg!!\n");
+ return BC_STS_INV_ARG;
+ }
+
+ tmp = crystalhd_alloc_elem(ioq->adp);
+ if (!tmp) {
+ BCMLOG_ERR("No free elements.\n");
+ return BC_STS_INSUFF_RES;
+ }
+
+ tmp->data = data;
+ tmp->tag = tag;
+ spin_lock_irqsave(&ioq->lock, flags);
+ tmp->flink = (crystalhd_elem_t *)&ioq->head;
+ tmp->blink = ioq->tail;
+ tmp->flink->blink = tmp;
+ tmp->blink->flink = tmp;
+ ioq->count++;
+ spin_unlock_irqrestore(&ioq->lock, flags);
+
+ if (wake)
+ crystalhd_set_event(&ioq->event);
+
+ return BC_STS_SUCCESS;
+}
+
+/**
+ * crystalhd_dioq_fetch - Fetch element from head.
+ * @ioq: DIO queue instance
+ *
+ * Return:
+ * data element from the head..
+ *
+ * Remove an element from Queue.
+ */
+void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq)
+{
+ unsigned long flags = 0;
+ crystalhd_elem_t *tmp;
+ crystalhd_elem_t *ret = NULL;
+ void *data = NULL;
+
+ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
+ BCMLOG_ERR("Invalid arg!!\n");
+ return data;
+ }
+
+ spin_lock_irqsave(&ioq->lock, flags);
+ tmp = ioq->head;
+ if (tmp != (crystalhd_elem_t *)&ioq->head) {
+ ret = tmp;
+ tmp->flink->blink = tmp->blink;
+ tmp->blink->flink = tmp->flink;
+ ioq->count--;
+ }
+ spin_unlock_irqrestore(&ioq->lock, flags);
+ if (ret) {
+ data = ret->data;
+ crystalhd_free_elem(ioq->adp, ret);
+ }
+
+ return data;
+}
+/**
+ * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
+ * @ioq: DIO queue instance
+ * @tag: Tag to search for.
+ *
+ * Return:
+ * element from the head..
+ *
+ * Search TAG and remove the element.
+ */
+void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag)
+{
+ unsigned long flags = 0;
+ crystalhd_elem_t *tmp;
+ crystalhd_elem_t *ret = NULL;
+ void *data = NULL;
+
+ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
+ BCMLOG_ERR("Invalid arg!!\n");
+ return data;
+ }
+
+ spin_lock_irqsave(&ioq->lock, flags);
+ tmp = ioq->head;
+ while (tmp != (crystalhd_elem_t *)&ioq->head) {
+ if (tmp->tag == tag) {
+ ret = tmp;
+ tmp->flink->blink = tmp->blink;
+ tmp->blink->flink = tmp->flink;
+ ioq->count--;
+ break;
+ }
+ tmp = tmp->flink;
+ }
+ spin_unlock_irqrestore(&ioq->lock, flags);
+
+ if (ret) {
+ data = ret->data;
+ crystalhd_free_elem(ioq->adp, ret);
+ }
+
+ return data;
+}
+
+/**
+ * crystalhd_dioq_fetch_wait - Fetch element from Head.
+ * @ioq: DIO queue instance
+ * @to_secs: Wait timeout in seconds..
+ *
+ * Return:
+ * element from the head..
+ *
+ * Return element from head if Q is not empty. Wait for new element
+ * if Q is empty for Timeout seconds.
+ */
+void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs,
+ uint32_t *sig_pend)
+{
+ unsigned long flags = 0;
+ int rc = 0, count;
+ void *tmp = NULL;
+
+ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
+ BCMLOG_ERR("Invalid arg!!\n");
+ return tmp;
+ }
+
+ count = to_secs;
+ spin_lock_irqsave(&ioq->lock, flags);
+ while ((ioq->count == 0) && count) {
+ spin_unlock_irqrestore(&ioq->lock, flags);
+
+ crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
+ if (rc == 0) {
+ goto out;
+ } else if (rc == -EINTR) {
+ BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
+ *sig_pend = 1;
+ return tmp;
+ }
+ spin_lock_irqsave(&ioq->lock, flags);
+ count--;
+ }
+ spin_unlock_irqrestore(&ioq->lock, flags);
+
+out:
+ return crystalhd_dioq_fetch(ioq);
+}
+
+/**
+ * crystalhd_map_dio - Map user address for DMA
+ * @adp: Adapter instance
+ * @ubuff: User buffer to map.
+ * @ubuff_sz: User buffer size.
+ * @uv_offset: UV buffer offset.
+ * @en_422mode: TRUE:422 FALSE:420 Capture mode.
+ * @dir_tx: TRUE for Tx (To device from host)
+ * @dio_hnd: Handle to mapped DIO request.
+ *
+ * Return:
+ * Status.
+ *
+ * This routine maps user address and lock pages for DMA.
+ *
+ */
+BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
+ uint32_t ubuff_sz, uint32_t uv_offset,
+ bool en_422mode, bool dir_tx,
+ crystalhd_dio_req **dio_hnd)
+{
+ crystalhd_dio_req *dio;
+ /* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
+ unsigned long start = 0, end = 0, uaddr = 0, count = 0;
+ unsigned long spsz = 0, uv_start = 0;
+ int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
+
+ if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+ /* Compute pages */
+ uaddr = (unsigned long)ubuff;
+ count = (unsigned long)ubuff_sz;
+ end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ start = uaddr >> PAGE_SHIFT;
+ nr_pages = end - start;
+
+ if (!count || ((uaddr + count) < uaddr)) {
+ BCMLOG_ERR("User addr overflow!!\n");
+ return BC_STS_INV_ARG;
+ }
+
+ dio = crystalhd_alloc_dio(adp);
+ if (!dio) {
+ BCMLOG_ERR("dio pool empty..\n");
+ return BC_STS_INSUFF_RES;
+ }
+
+ if (dir_tx) {
+ rw = WRITE;
+ dio->direction = DMA_TO_DEVICE;
+ } else {
+ rw = READ;
+ dio->direction = DMA_FROM_DEVICE;
+ }
+
+ if (nr_pages > dio->max_pages) {
+ BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
+ dio->max_pages, nr_pages);
+ crystalhd_unmap_dio(adp, dio);
+ return BC_STS_INSUFF_RES;
+ }
+
+ if (uv_offset) {
+ uv_start = (uaddr + (unsigned long)uv_offset) >> PAGE_SHIFT;
+ dio->uinfo.uv_sg_ix = uv_start - start;
+ dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
+ }
+
+ dio->fb_size = ubuff_sz & 0x03;
+ if (dio->fb_size) {
+ res = copy_from_user(dio->fb_va,
+ (void *)(uaddr + count - dio->fb_size),
+ dio->fb_size);
+ if (res) {
+ BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
+ res, dio->fb_size,
+ (void *)(uaddr + count-dio->fb_size));
+ crystalhd_unmap_dio(adp, dio);
+ return BC_STS_INSUFF_RES;
+ }
+ }
+
+ down_read(&current->mm->mmap_sem);
+ res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
+ 0, dio->pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ /* Save for release..*/
+ dio->sig = crystalhd_dio_locked;
+ if (res < nr_pages) {
+ BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
+ dio->page_cnt = res;
+ crystalhd_unmap_dio(adp, dio);
+ return BC_STS_ERROR;
+ }
+
+ dio->page_cnt = nr_pages;
+ /* Get scatter/gather */
+ crystalhd_init_sg(dio->sg, dio->page_cnt);
+ crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
+ if (nr_pages > 1) {
+ dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
+
+#ifdef CONFIG_X86_64
+ dio->sg[0].dma_length = dio->sg[0].length;
+#endif
+ count -= dio->sg[0].length;
+ for (i = 1; i < nr_pages; i++) {
+ if (count < 4) {
+ spsz = count;
+ skip_fb_sg = 1;
+ } else {
+ spsz = (count < PAGE_SIZE) ?
+ (count & ~0x03) : PAGE_SIZE;
+ }
+ crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
+ count -= spsz;
+ }
+ } else {
+ if (count < 4) {
+ dio->sg[0].length = count;
+ skip_fb_sg = 1;
+ } else {
+ dio->sg[0].length = count - dio->fb_size;
+ }
+#ifdef CONFIG_X86_64
+ dio->sg[0].dma_length = dio->sg[0].length;
+#endif
+ }
+ dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
+ dio->page_cnt, dio->direction);
+ if (dio->sg_cnt <= 0) {
+ BCMLOG_ERR("sg map %d-%d \n", dio->sg_cnt, dio->page_cnt);
+ crystalhd_unmap_dio(adp, dio);
+ return BC_STS_ERROR;
+ }
+ if (dio->sg_cnt && skip_fb_sg)
+ dio->sg_cnt -= 1;
+ dio->sig = crystalhd_dio_sg_mapped;
+ /* Fill in User info.. */
+ dio->uinfo.xfr_len = ubuff_sz;
+ dio->uinfo.xfr_buff = ubuff;
+ dio->uinfo.uv_offset = uv_offset;
+ dio->uinfo.b422mode = en_422mode;
+ dio->uinfo.dir_tx = dir_tx;
+
+ *dio_hnd = dio;
+
+ return BC_STS_SUCCESS;
+}
+
+/**
+ * crystalhd_unmap_sgl - Release mapped resources
+ * @adp: Adapter instance
+ * @dio: DIO request instance
+ *
+ * Return:
+ * Status.
+ *
+ * This routine is to unmap the user buffer pages.
+ */
+BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
+{
+ struct page *page = NULL;
+ int j = 0;
+
+ if (!adp || !dio) {
+ BCMLOG_ERR("Invalid arg \n");
+ return BC_STS_INV_ARG;
+ }
+
+ if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
+ for (j = 0; j < dio->page_cnt; j++) {
+ page = dio->pages[j];
+ if (page) {
+ if (!PageReserved(page) &&
+ (dio->direction == DMA_FROM_DEVICE))
+ SetPageDirty(page);
+ page_cache_release(page);
+ }
+ }
+ }
+ if (dio->sig == crystalhd_dio_sg_mapped)
+ pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
+
+ crystalhd_free_dio(adp, dio);
+
+ return BC_STS_SUCCESS;
+}
+
+/**
+ * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
+ * @adp: Adapter instance
+ * @max_pages: Max pages for size calculation.
+ *
+ * Return:
+ * system error.
+ *
+ * This routine creates a memory pool to hold dio context for
+ * for HW Direct IO operation.
+ */
+int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
+{
+ uint32_t asz = 0, i = 0;
+ uint8_t *temp;
+ crystalhd_dio_req *dio;
+
+ if (!adp || !max_pages) {
+ BCMLOG_ERR("Invalid Arg!!\n");
+ return -EINVAL;
+ }
+
+ /* Get dma memory for fill byte handling..*/
+ adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
+ adp->pdev, 8, 8, 0);
+ if (!adp->fill_byte_pool) {
+ BCMLOG_ERR("failed to create fill byte pool\n");
+ return -ENOMEM;
+ }
+
+ /* Get the max size from user based on 420/422 modes */
+ asz = (sizeof(*dio->pages) * max_pages) +
+ (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
+
+ BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
+ BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
+
+ for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
+ temp = (uint8_t *)kzalloc(asz, GFP_KERNEL);
+ if ((temp) == NULL) {
+ BCMLOG_ERR("Failed to alloc %d mem\n", asz);
+ return -ENOMEM;
+ }
+
+ dio = (crystalhd_dio_req *)temp;
+ temp += sizeof(*dio);
+ dio->pages = (struct page **)temp;
+ temp += (sizeof(*dio->pages) * max_pages);
+ dio->sg = (struct scatterlist *)temp;
+ dio->max_pages = max_pages;
+ dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
+ &dio->fb_pa);
+ if (!dio->fb_va) {
+ BCMLOG_ERR("fill byte alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ crystalhd_free_dio(adp, dio);
+ }
+
+ return 0;
+}
+
+/**
+ * crystalhd_destroy_dio_pool - Release DIO mem pool.
+ * @adp: Adapter instance
+ *
+ * Return:
+ * none.
+ *
+ * This routine releases dio memory pool during close.
+ */
+void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
+{
+ crystalhd_dio_req *dio;
+ int count = 0;
+
+ if (!adp) {
+ BCMLOG_ERR("Invalid Arg!!\n");
+ return;
+ }
+
+ do {
+ dio = crystalhd_alloc_dio(adp);
+ if (dio) {
+ if (dio->fb_va)
+ pci_pool_free(adp->fill_byte_pool,
+ dio->fb_va, dio->fb_pa);
+ count++;
+ kfree(dio);
+ }
+ } while (dio);
+
+ if (adp->fill_byte_pool) {
+ pci_pool_destroy(adp->fill_byte_pool);
+ adp->fill_byte_pool = NULL;
+ }
+
+ BCMLOG(BCMLOG_DBG, "Released dio pool %d \n", count);
+}
+
+/**
+ * crystalhd_create_elem_pool - List element pool creation.
+ * @adp: Adapter instance
+ * @pool_size: Number of elements in the pool.
+ *
+ * Return:
+ * 0 - success, <0 error
+ *
+ * Create general purpose list element pool to hold pending,
+ * and active requests.
+ */
+int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
+ uint32_t pool_size)
+{
+ uint32_t i;
+ crystalhd_elem_t *temp;
+
+ if (!adp || !pool_size)
+ return -EINVAL;
+
+ for (i = 0; i < pool_size; i++) {
+ temp = kzalloc(sizeof(*temp), GFP_KERNEL);
+ if (!temp) {
+ BCMLOG_ERR("kalloc failed \n");
+ return -ENOMEM;
+ }
+ crystalhd_free_elem(adp, temp);
+ }
+ BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
+ return 0;
+}
+
+/**
+ * crystalhd_delete_elem_pool - List element pool deletion.
+ * @adp: Adapter instance
+ *
+ * Return:
+ * none
+ *
+ * Delete general purpose list element pool.
+ */
+void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
+{
+ crystalhd_elem_t *temp;
+ int dbg_cnt = 0;
+
+ if (!adp)
+ return;
+
+ do {
+ temp = crystalhd_alloc_elem(adp);
+ if (temp) {
+ kfree(temp);
+ dbg_cnt++;
+ }
+ } while (temp);
+
+ BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
+}
+
+/*================ Debug support routines.. ================================*/
+void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
+{
+ uint32_t i, k = 1;
+
+ for (i = 0; i < dwcount; i++) {
+ if (k == 1)
+ BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
+
+ BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
+
+ buff += sizeof(uint32_t);
+ off += sizeof(uint32_t);
+ k++;
+ if ((i == dwcount - 1) || (k > 4)) {
+ BCMLOG(BCMLOG_DATA, "\n");
+ k = 1;
+ }
+ }
+}