diff options
author | David S. Miller <davem@davemloft.net> | 2010-03-15 23:23:54 (GMT) |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-15 23:23:54 (GMT) |
commit | 4961e02f1999e1c3468c09b2669c94d7c3ae82a8 (patch) | |
tree | 44c15abb09d7ba5e17a9aba95ee246648b1c1a8a /drivers/staging/crystalhd/crystalhd_misc.c | |
parent | d14a0ebda7d3daede1a99c01527affb9ceaa4c22 (diff) | |
parent | a3d3203e4bb40f253b1541e310dc0f9305be7c84 (diff) | |
download | linux-fsl-qoriq-4961e02f1999e1c3468c09b2669c94d7c3ae82a8.tar.xz |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'drivers/staging/crystalhd/crystalhd_misc.c')
-rw-r--r-- | drivers/staging/crystalhd/crystalhd_misc.c | 1030 |
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 0000000..587dcc4 --- /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(¤t->mm->mmap_sem); + res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ, + 0, dio->pages, NULL); + up_read(¤t->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; + } + } +} |