diff options
author | Jarod Wilson <jarod@redhat.com> | 2010-01-04 18:02:27 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-03 16:42:34 -0800 |
commit | 7963eb432be2ebbf7e740bcdf6b385cc68fb7627 (patch) | |
tree | be31cb0fdac96726e5bfe983f85f6874d97f4cba /drivers/staging/crystalhd/crystalhd_misc.c | |
parent | ca1f29c01aaa37d522dc6ce311769731f5f1919e (diff) |
staging: add Broadcom Crystal HD driver
This patch supersedes the earlier ones sent by Manu Abraham to add
the Broadcom Crystal HD driver to the staging tree, per discussion
with him about it. I've been working with Broadcom's Naren Sankar
on this driver for a number of months, and had already talked Naren
about submitting this on Broadcom's behalf, didn't expect anyone
else to jump on submitting it as quickly as Manu did. ;)
This version is a one-shot deal, incorporating the original driver,
Manu's coding style clean-ups, udev device creation support from
Edgar 'gimli' Hucek, and a number of other small tweaks from myself
and Scott Davilla, the other individual who has been working closely
on this code with Naren and I.
I've tested this iteration of the code lightly on a mini pci-e board
in a ThinkPad T61p running x86_64 Fedora 12, with the expected results,
and will test further on other systems with other variants of the card
(I have three varieties of this device currently in hand). Scott has
also tested on assorted primarily i686 varieties of Ubuntu, and Naren
has tested with both Fedora and Ubuntu, iirc.
Note: only the 70012 is currently supported by this driver, 70015
support will follow later. Also note that Blu-Ray support isn't
enabled (at the firmware level), due to misc fun related to the
BD encryption scheme, DRM, etc. :\
I *do* have a git tree containing the driver, lib, gst plugin and
firmware that I'm working from at the moment[*], as there are inter-
dependencies between the driver and lib, and the driver can be used
with kernels going a ways back (I've only tested back to 2.6.18 as
it exists in Red Hat Enterprise Linux 5). I'm exporting from there,
into a linux-next tree, then generating patches from there. The goal
is to feed everything upstream as quickly as possible, but there are
users who want this code for earlier kernels too...
The firmware will be submitted for inclusion in dwmw2's linux-firmware
tree once there is a suitable redistribution-no-modification type of
license on it (I believe Naren is working with Broadcom legal to get
that in place).
Changelog from initial Broadcom release to here:
commit d20475d444610c5683d09e63f707f5bb22359062
Author: Jarod Wilson <jarod@redhat.com>
Date: Mon Jan 4 13:55:16 2010 -0500
include: lib doesn't build w/o the removed stdint include
So add it back...
Signed-off-by: Jarod Wilson <jarod@redhat.com>
commit c181070a330530b792d2b80e3ec6ab12a5a57394
Author: Scott Davilla <davilla@4pi.com>
Date: Mon Jan 4 13:38:37 2010 -0500
include: don't define VOID if its already defined
Signed-off-by: Scott Davilla <davilla@4pi.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
commit 33d8a2b691e81212e398f53770578d79650bf0bc
Author: Jarod Wilson <jarod@redhat.com>
Date: Mon Jan 4 13:12:10 2010 -0500
driver: create crystalhd device using udev
Based on:
http://sourceforge.net/apps/trac/archvdr/browser/trunk/archvdr/crystalhd/use_udev.patch
Signed-off-by: Edgar ( gimli ) Hucek <ebsi4711 at gmail dot com>
Formatting tweaks, error-handling path fixups and any bugs added by Jarod.
Signed-off-by: Jarod Wilson <jarod@redhat.com>
commit c44c64dea5537814796fcbe2d9db0209383c78b9
Author: Manu Abraham <abraham.manu@gmail.com>
Date: Mon Jan 4 10:32:47 2010 -0500
crystalhd: coding style cleanups
Signed-off-by: Manu Abraham <abraham.manu@gmail.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
commit cffa6da7467ff697a656d1dfff54bb0513a053dc
Author: Jarod Wilson <jarod@redhat.com>
Date: Mon Jan 4 10:17:27 2010 -0500
crystalhd: run dos2unix over everything, this is linux source...
Signed-off-by: Jarod Wilson <jarod@redhat.com>
commit 7fa38a282db7af5a5746055f7c6cef8a9b8ee138
Author: Jarod Wilson <jarod@redhat.com>
Date: Mon Jan 4 10:02:33 2010 -0500
crystalhd: initial import of released Broadcom code
Straight import of:
http://www.broadcom.com/docs/support/crystalhd/crystalhd_linux_20091229.zip
Unfortunately, we're unable to publicly publish all the history that got
us from the initial internal code to what was released here, but such is
life, we can just be happy we've got this open-sourced now. :)
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Naren Sankar <nsankar@broadcom.com>
Signed-off-by: Scott Davilla <davilla@4pi.com>
Signed-off-by: Manu Abraham <abraham.manu@gmail.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/crystalhd/crystalhd_misc.c')
-rw-r--r-- | drivers/staging/crystalhd/crystalhd_misc.c | 1029 |
1 files changed, 1029 insertions, 0 deletions
diff --git a/drivers/staging/crystalhd/crystalhd_misc.c b/drivers/staging/crystalhd/crystalhd_misc.c new file mode 100644 index 00000000000..32e632c2ab3 --- /dev/null +++ b/drivers/staging/crystalhd/crystalhd_misc.c @@ -0,0 +1,1029 @@ +/*************************************************************************** + * 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 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; + } + } +} |