![Lior Dotan](/assets/img/avatar_default.png)
Remove typedefs from driver Signed-of-by: Lior Dotan <liodot@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2393 lines
63 KiB
C
2393 lines
63 KiB
C
/***************************************************************************
|
|
* Copyright (c) 2005-2009, Broadcom Corporation.
|
|
*
|
|
* Name: crystalhd_hw . c
|
|
*
|
|
* Description:
|
|
* BCM70010 Linux driver HW layer.
|
|
*
|
|
**********************************************************************
|
|
* 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 <linux/pci.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include "crystalhd_hw.h"
|
|
|
|
/* Functions internal to this file */
|
|
|
|
static void crystalhd_enable_uarts(struct crystalhd_adp *adp)
|
|
{
|
|
bc_dec_reg_wr(adp, UartSelectA, BSVS_UART_STREAM);
|
|
bc_dec_reg_wr(adp, UartSelectB, BSVS_UART_DEC_OUTER);
|
|
}
|
|
|
|
|
|
static void crystalhd_start_dram(struct crystalhd_adp *adp)
|
|
{
|
|
bc_dec_reg_wr(adp, SDRAM_PARAM, ((40 / 5 - 1) << 0) |
|
|
/* tras (40ns tras)/(5ns period) -1 ((15/5 - 1) << 4) | // trcd */
|
|
((15 / 5 - 1) << 7) | /* trp */
|
|
((10 / 5 - 1) << 10) | /* trrd */
|
|
((15 / 5 + 1) << 12) | /* twr */
|
|
((2 + 1) << 16) | /* twtr */
|
|
((70 / 5 - 2) << 19) | /* trfc */
|
|
(0 << 23));
|
|
|
|
bc_dec_reg_wr(adp, SDRAM_PRECHARGE, 0);
|
|
bc_dec_reg_wr(adp, SDRAM_EXT_MODE, 2);
|
|
bc_dec_reg_wr(adp, SDRAM_MODE, 0x132);
|
|
bc_dec_reg_wr(adp, SDRAM_PRECHARGE, 0);
|
|
bc_dec_reg_wr(adp, SDRAM_REFRESH, 0);
|
|
bc_dec_reg_wr(adp, SDRAM_REFRESH, 0);
|
|
bc_dec_reg_wr(adp, SDRAM_MODE, 0x32);
|
|
/* setting the refresh rate here */
|
|
bc_dec_reg_wr(adp, SDRAM_REF_PARAM, ((1 << 12) | 96));
|
|
}
|
|
|
|
|
|
static bool crystalhd_bring_out_of_rst(struct crystalhd_adp *adp)
|
|
{
|
|
union link_misc_perst_deco_ctrl rst_deco_cntrl;
|
|
union link_misc_perst_clk_ctrl rst_clk_cntrl;
|
|
uint32_t temp;
|
|
|
|
/*
|
|
* Link clocks: MISC_PERST_CLOCK_CTRL Clear PLL power down bit,
|
|
* delay to allow PLL to lock Clear alternate clock, stop clock bits
|
|
*/
|
|
rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
|
|
rst_clk_cntrl.pll_pwr_dn = 0;
|
|
crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
|
|
rst_clk_cntrl.stop_core_clk = 0;
|
|
rst_clk_cntrl.sel_alt_clk = 0;
|
|
|
|
crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
/*
|
|
* Bus Arbiter Timeout: GISB_ARBITER_TIMER
|
|
* Set internal bus arbiter timeout to 40us based on core clock speed
|
|
* (63MHz * 40us = 0x9D8)
|
|
*/
|
|
crystalhd_reg_wr(adp, GISB_ARBITER_TIMER, 0x9D8);
|
|
|
|
/*
|
|
* Decoder clocks: MISC_PERST_DECODER_CTRL
|
|
* Enable clocks while 7412 reset is asserted, delay
|
|
* De-assert 7412 reset
|
|
*/
|
|
rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
|
|
rst_deco_cntrl.stop_bcm_7412_clk = 0;
|
|
rst_deco_cntrl.bcm7412_rst = 1;
|
|
crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
|
|
msleep_interruptible(10);
|
|
|
|
rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
|
|
rst_deco_cntrl.bcm7412_rst = 0;
|
|
crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
/* Disable OTP_CONTENT_MISC to 0 to disable all secure modes */
|
|
crystalhd_reg_wr(adp, OTP_CONTENT_MISC, 0);
|
|
|
|
/* Clear bit 29 of 0x404 */
|
|
temp = crystalhd_reg_rd(adp, PCIE_TL_TRANSACTION_CONFIGURATION);
|
|
temp &= ~BC_BIT(29);
|
|
crystalhd_reg_wr(adp, PCIE_TL_TRANSACTION_CONFIGURATION, temp);
|
|
|
|
/* 2.5V regulator must be set to 2.6 volts (+6%) */
|
|
/* FIXME: jarod: what's the point of this reg read? */
|
|
temp = crystalhd_reg_rd(adp, MISC_PERST_VREG_CTRL);
|
|
crystalhd_reg_wr(adp, MISC_PERST_VREG_CTRL, 0xF3);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool crystalhd_put_in_reset(struct crystalhd_adp *adp)
|
|
{
|
|
union link_misc_perst_deco_ctrl rst_deco_cntrl;
|
|
union link_misc_perst_clk_ctrl rst_clk_cntrl;
|
|
uint32_t temp;
|
|
|
|
/*
|
|
* Decoder clocks: MISC_PERST_DECODER_CTRL
|
|
* Assert 7412 reset, delay
|
|
* Assert 7412 stop clock
|
|
*/
|
|
rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
|
|
rst_deco_cntrl.stop_bcm_7412_clk = 1;
|
|
crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
/* Bus Arbiter Timeout: GISB_ARBITER_TIMER
|
|
* Set internal bus arbiter timeout to 40us based on core clock speed
|
|
* (6.75MHZ * 40us = 0x10E)
|
|
*/
|
|
crystalhd_reg_wr(adp, GISB_ARBITER_TIMER, 0x10E);
|
|
|
|
/* Link clocks: MISC_PERST_CLOCK_CTRL
|
|
* Stop core clk, delay
|
|
* Set alternate clk, delay, set PLL power down
|
|
*/
|
|
rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
|
|
rst_clk_cntrl.stop_core_clk = 1;
|
|
rst_clk_cntrl.sel_alt_clk = 1;
|
|
crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
|
|
rst_clk_cntrl.pll_pwr_dn = 1;
|
|
crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
|
|
|
|
/*
|
|
* Read and restore the Transaction Configuration Register
|
|
* after core reset
|
|
*/
|
|
temp = crystalhd_reg_rd(adp, PCIE_TL_TRANSACTION_CONFIGURATION);
|
|
|
|
/*
|
|
* Link core soft reset: MISC3_RESET_CTRL
|
|
* - Write BIT[0]=1 and read it back for core reset to take place
|
|
*/
|
|
crystalhd_reg_wr(adp, MISC3_RESET_CTRL, 1);
|
|
rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC3_RESET_CTRL);
|
|
msleep_interruptible(50);
|
|
|
|
/* restore the transaction configuration register */
|
|
crystalhd_reg_wr(adp, PCIE_TL_TRANSACTION_CONFIGURATION, temp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void crystalhd_disable_interrupts(struct crystalhd_adp *adp)
|
|
{
|
|
union intr_mask_reg intr_mask;
|
|
intr_mask.whole_reg = crystalhd_reg_rd(adp, INTR_INTR_MSK_STS_REG);
|
|
intr_mask.mask_pcie_err = 1;
|
|
intr_mask.mask_pcie_rbusmast_err = 1;
|
|
intr_mask.mask_pcie_rgr_bridge = 1;
|
|
intr_mask.mask_rx_done = 1;
|
|
intr_mask.mask_rx_err = 1;
|
|
intr_mask.mask_tx_done = 1;
|
|
intr_mask.mask_tx_err = 1;
|
|
crystalhd_reg_wr(adp, INTR_INTR_MSK_SET_REG, intr_mask.whole_reg);
|
|
|
|
return;
|
|
}
|
|
|
|
static void crystalhd_enable_interrupts(struct crystalhd_adp *adp)
|
|
{
|
|
union intr_mask_reg intr_mask;
|
|
intr_mask.whole_reg = crystalhd_reg_rd(adp, INTR_INTR_MSK_STS_REG);
|
|
intr_mask.mask_pcie_err = 1;
|
|
intr_mask.mask_pcie_rbusmast_err = 1;
|
|
intr_mask.mask_pcie_rgr_bridge = 1;
|
|
intr_mask.mask_rx_done = 1;
|
|
intr_mask.mask_rx_err = 1;
|
|
intr_mask.mask_tx_done = 1;
|
|
intr_mask.mask_tx_err = 1;
|
|
crystalhd_reg_wr(adp, INTR_INTR_MSK_CLR_REG, intr_mask.whole_reg);
|
|
|
|
return;
|
|
}
|
|
|
|
static void crystalhd_clear_errors(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* FIXME: jarod: wouldn't we want to write a 0 to the reg? Or does the write clear the bits specified? */
|
|
reg = crystalhd_reg_rd(adp, MISC1_Y_RX_ERROR_STATUS);
|
|
if (reg)
|
|
crystalhd_reg_wr(adp, MISC1_Y_RX_ERROR_STATUS, reg);
|
|
|
|
reg = crystalhd_reg_rd(adp, MISC1_UV_RX_ERROR_STATUS);
|
|
if (reg)
|
|
crystalhd_reg_wr(adp, MISC1_UV_RX_ERROR_STATUS, reg);
|
|
|
|
reg = crystalhd_reg_rd(adp, MISC1_TX_DMA_ERROR_STATUS);
|
|
if (reg)
|
|
crystalhd_reg_wr(adp, MISC1_TX_DMA_ERROR_STATUS, reg);
|
|
}
|
|
|
|
static void crystalhd_clear_interrupts(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t intr_sts = crystalhd_reg_rd(adp, INTR_INTR_STATUS);
|
|
|
|
if (intr_sts) {
|
|
crystalhd_reg_wr(adp, INTR_INTR_CLR_REG, intr_sts);
|
|
|
|
/* Write End Of Interrupt for PCIE */
|
|
crystalhd_reg_wr(adp, INTR_EOI_CTRL, 1);
|
|
}
|
|
}
|
|
|
|
static void crystalhd_soft_rst(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t val;
|
|
|
|
/* Assert c011 soft reset*/
|
|
bc_dec_reg_wr(adp, DecHt_HostSwReset, 0x00000001);
|
|
msleep_interruptible(50);
|
|
|
|
/* Release c011 soft reset*/
|
|
bc_dec_reg_wr(adp, DecHt_HostSwReset, 0x00000000);
|
|
|
|
/* Disable Stuffing..*/
|
|
val = crystalhd_reg_rd(adp, MISC2_GLOBAL_CTRL);
|
|
val |= BC_BIT(8);
|
|
crystalhd_reg_wr(adp, MISC2_GLOBAL_CTRL, val);
|
|
}
|
|
|
|
static bool crystalhd_load_firmware_config(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t i = 0, reg;
|
|
|
|
crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (BC_DRAM_FW_CFG_ADDR >> 19));
|
|
|
|
crystalhd_reg_wr(adp, AES_CMD, 0);
|
|
crystalhd_reg_wr(adp, AES_CONFIG_INFO, (BC_DRAM_FW_CFG_ADDR & 0x7FFFF));
|
|
crystalhd_reg_wr(adp, AES_CMD, 0x1);
|
|
|
|
/* FIXME: jarod: I've seen this fail, and introducing extra delays helps... */
|
|
for (i = 0; i < 100; ++i) {
|
|
reg = crystalhd_reg_rd(adp, AES_STATUS);
|
|
if (reg & 0x1)
|
|
return true;
|
|
msleep_interruptible(10);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool crystalhd_start_device(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t dbg_options, glb_cntrl = 0, reg_pwrmgmt = 0;
|
|
|
|
BCMLOG(BCMLOG_INFO, "Starting BCM70012 Device\n");
|
|
|
|
reg_pwrmgmt = crystalhd_reg_rd(adp, PCIE_DLL_DATA_LINK_CONTROL);
|
|
reg_pwrmgmt &= ~ASPM_L1_ENABLE;
|
|
|
|
crystalhd_reg_wr(adp, PCIE_DLL_DATA_LINK_CONTROL, reg_pwrmgmt);
|
|
|
|
if (!crystalhd_bring_out_of_rst(adp)) {
|
|
BCMLOG_ERR("Failed To Bring Link Out Of Reset\n");
|
|
return false;
|
|
}
|
|
|
|
crystalhd_disable_interrupts(adp);
|
|
|
|
crystalhd_clear_errors(adp);
|
|
|
|
crystalhd_clear_interrupts(adp);
|
|
|
|
crystalhd_enable_interrupts(adp);
|
|
|
|
/* Enable the option for getting the total no. of DWORDS
|
|
* that have been transfered by the RXDMA engine
|
|
*/
|
|
dbg_options = crystalhd_reg_rd(adp, MISC1_DMA_DEBUG_OPTIONS_REG);
|
|
dbg_options |= 0x10;
|
|
crystalhd_reg_wr(adp, MISC1_DMA_DEBUG_OPTIONS_REG, dbg_options);
|
|
|
|
/* Enable PCI Global Control options */
|
|
glb_cntrl = crystalhd_reg_rd(adp, MISC2_GLOBAL_CTRL);
|
|
glb_cntrl |= 0x100;
|
|
glb_cntrl |= 0x8000;
|
|
crystalhd_reg_wr(adp, MISC2_GLOBAL_CTRL, glb_cntrl);
|
|
|
|
crystalhd_enable_interrupts(adp);
|
|
|
|
crystalhd_soft_rst(adp);
|
|
crystalhd_start_dram(adp);
|
|
crystalhd_enable_uarts(adp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool crystalhd_stop_device(struct crystalhd_adp *adp)
|
|
{
|
|
uint32_t reg;
|
|
|
|
BCMLOG(BCMLOG_INFO, "Stopping BCM70012 Device\n");
|
|
/* Clear and disable interrupts */
|
|
crystalhd_disable_interrupts(adp);
|
|
crystalhd_clear_errors(adp);
|
|
crystalhd_clear_interrupts(adp);
|
|
|
|
if (!crystalhd_put_in_reset(adp))
|
|
BCMLOG_ERR("Failed to Put Link To Reset State\n");
|
|
|
|
reg = crystalhd_reg_rd(adp, PCIE_DLL_DATA_LINK_CONTROL);
|
|
reg |= ASPM_L1_ENABLE;
|
|
crystalhd_reg_wr(adp, PCIE_DLL_DATA_LINK_CONTROL, reg);
|
|
|
|
/* Set PCI Clk Req */
|
|
reg = crystalhd_reg_rd(adp, PCIE_CLK_REQ_REG);
|
|
reg |= PCI_CLK_REQ_ENABLE;
|
|
crystalhd_reg_wr(adp, PCIE_CLK_REQ_REG, reg);
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct crystalhd_rx_dma_pkt *crystalhd_hw_alloc_rx_pkt(struct crystalhd_hw *hw)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct crystalhd_rx_dma_pkt *temp = NULL;
|
|
|
|
if (!hw)
|
|
return NULL;
|
|
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
temp = hw->rx_pkt_pool_head;
|
|
if (temp) {
|
|
hw->rx_pkt_pool_head = hw->rx_pkt_pool_head->next;
|
|
temp->dio_req = NULL;
|
|
temp->pkt_tag = 0;
|
|
temp->flags = 0;
|
|
}
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
|
|
return temp;
|
|
}
|
|
|
|
static void crystalhd_hw_free_rx_pkt(struct crystalhd_hw *hw,
|
|
struct crystalhd_rx_dma_pkt *pkt)
|
|
{
|
|
unsigned long flags = 0;
|
|
|
|
if (!hw || !pkt)
|
|
return;
|
|
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
pkt->next = hw->rx_pkt_pool_head;
|
|
hw->rx_pkt_pool_head = pkt;
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
}
|
|
|
|
/*
|
|
* Call back from TX - IOQ deletion.
|
|
*
|
|
* This routine will release the TX DMA rings allocated
|
|
* druing setup_dma rings interface.
|
|
*
|
|
* Memory is allocated per DMA ring basis. This is just
|
|
* a place holder to be able to create the dio queues.
|
|
*/
|
|
static void crystalhd_tx_desc_rel_call_back(void *context, void *data)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Rx Packet release callback..
|
|
*
|
|
* Release All user mapped capture buffers and Our DMA packets
|
|
* back to our free pool. The actual cleanup of the DMA
|
|
* ring descriptors happen during dma ring release.
|
|
*/
|
|
static void crystalhd_rx_pkt_rel_call_back(void *context, void *data)
|
|
{
|
|
struct crystalhd_hw *hw = (struct crystalhd_hw *)context;
|
|
struct crystalhd_rx_dma_pkt *pkt = (struct crystalhd_rx_dma_pkt *)data;
|
|
|
|
if (!pkt || !hw) {
|
|
BCMLOG_ERR("Invalid arg - %p %p\n", hw, pkt);
|
|
return;
|
|
}
|
|
|
|
if (pkt->dio_req)
|
|
crystalhd_unmap_dio(hw->adp, pkt->dio_req);
|
|
else
|
|
BCMLOG_ERR("Missing dio_req: 0x%x\n", pkt->pkt_tag);
|
|
|
|
crystalhd_hw_free_rx_pkt(hw, pkt);
|
|
}
|
|
|
|
#define crystalhd_hw_delete_ioq(adp, q) \
|
|
if (q) { \
|
|
crystalhd_delete_dioq(adp, q); \
|
|
q = NULL; \
|
|
}
|
|
|
|
static void crystalhd_hw_delete_ioqs(struct crystalhd_hw *hw)
|
|
{
|
|
if (!hw)
|
|
return;
|
|
|
|
BCMLOG(BCMLOG_DBG, "Deleting IOQs\n");
|
|
crystalhd_hw_delete_ioq(hw->adp, hw->tx_actq);
|
|
crystalhd_hw_delete_ioq(hw->adp, hw->tx_freeq);
|
|
crystalhd_hw_delete_ioq(hw->adp, hw->rx_actq);
|
|
crystalhd_hw_delete_ioq(hw->adp, hw->rx_freeq);
|
|
crystalhd_hw_delete_ioq(hw->adp, hw->rx_rdyq);
|
|
}
|
|
|
|
#define crystalhd_hw_create_ioq(sts, hw, q, cb) \
|
|
do { \
|
|
sts = crystalhd_create_dioq(hw->adp, &q, cb, hw); \
|
|
if (sts != BC_STS_SUCCESS) \
|
|
goto hw_create_ioq_err; \
|
|
} while (0)
|
|
|
|
/*
|
|
* Create IOQs..
|
|
*
|
|
* TX - Active & Free
|
|
* RX - Active, Ready and Free.
|
|
*/
|
|
static enum BC_STATUS crystalhd_hw_create_ioqs(struct crystalhd_hw *hw)
|
|
{
|
|
enum BC_STATUS sts = BC_STS_SUCCESS;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arg!!\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
crystalhd_hw_create_ioq(sts, hw, hw->tx_freeq,
|
|
crystalhd_tx_desc_rel_call_back);
|
|
crystalhd_hw_create_ioq(sts, hw, hw->tx_actq,
|
|
crystalhd_tx_desc_rel_call_back);
|
|
|
|
crystalhd_hw_create_ioq(sts, hw, hw->rx_freeq,
|
|
crystalhd_rx_pkt_rel_call_back);
|
|
crystalhd_hw_create_ioq(sts, hw, hw->rx_rdyq,
|
|
crystalhd_rx_pkt_rel_call_back);
|
|
crystalhd_hw_create_ioq(sts, hw, hw->rx_actq,
|
|
crystalhd_rx_pkt_rel_call_back);
|
|
|
|
return sts;
|
|
|
|
hw_create_ioq_err:
|
|
crystalhd_hw_delete_ioqs(hw);
|
|
|
|
return sts;
|
|
}
|
|
|
|
|
|
static bool crystalhd_code_in_full(struct crystalhd_adp *adp, uint32_t needed_sz,
|
|
bool b_188_byte_pkts, uint8_t flags)
|
|
{
|
|
uint32_t base, end, writep, readp;
|
|
uint32_t cpbSize, cpbFullness, fifoSize;
|
|
|
|
if (flags & 0x02) { /* ASF Bit is set */
|
|
base = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Base);
|
|
end = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2End);
|
|
writep = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Wrptr);
|
|
readp = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Rdptr);
|
|
} else if (b_188_byte_pkts) { /*Encrypted 188 byte packets*/
|
|
base = bc_dec_reg_rd(adp, REG_Dec_TsUser0Base);
|
|
end = bc_dec_reg_rd(adp, REG_Dec_TsUser0End);
|
|
writep = bc_dec_reg_rd(adp, REG_Dec_TsUser0Wrptr);
|
|
readp = bc_dec_reg_rd(adp, REG_Dec_TsUser0Rdptr);
|
|
} else {
|
|
base = bc_dec_reg_rd(adp, REG_DecCA_RegCinBase);
|
|
end = bc_dec_reg_rd(adp, REG_DecCA_RegCinEnd);
|
|
writep = bc_dec_reg_rd(adp, REG_DecCA_RegCinWrPtr);
|
|
readp = bc_dec_reg_rd(adp, REG_DecCA_RegCinRdPtr);
|
|
}
|
|
|
|
cpbSize = end - base;
|
|
if (writep >= readp)
|
|
cpbFullness = writep - readp;
|
|
else
|
|
cpbFullness = (end - base) - (readp - writep);
|
|
|
|
fifoSize = cpbSize - cpbFullness;
|
|
|
|
if (fifoSize < BC_INFIFO_THRESHOLD)
|
|
return true;
|
|
|
|
if (needed_sz > (fifoSize - BC_INFIFO_THRESHOLD))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_hw_tx_req_complete(struct crystalhd_hw *hw,
|
|
uint32_t list_id, enum BC_STATUS cs)
|
|
{
|
|
struct tx_dma_pkt *tx_req;
|
|
|
|
if (!hw || !list_id) {
|
|
BCMLOG_ERR("Invalid Arg..\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
hw->pwr_lock--;
|
|
|
|
tx_req = (struct tx_dma_pkt *)crystalhd_dioq_find_and_fetch(hw->tx_actq, list_id);
|
|
if (!tx_req) {
|
|
if (cs != BC_STS_IO_USER_ABORT)
|
|
BCMLOG_ERR("Find and Fetch Did not find req\n");
|
|
return BC_STS_NO_DATA;
|
|
}
|
|
|
|
if (tx_req->call_back) {
|
|
tx_req->call_back(tx_req->dio_req, tx_req->cb_event, cs);
|
|
tx_req->dio_req = NULL;
|
|
tx_req->cb_event = NULL;
|
|
tx_req->call_back = NULL;
|
|
} else {
|
|
BCMLOG(BCMLOG_DBG, "Missing Tx Callback - %X\n",
|
|
tx_req->list_tag);
|
|
}
|
|
|
|
/* Now put back the tx_list back in FreeQ */
|
|
tx_req->list_tag = 0;
|
|
|
|
return crystalhd_dioq_add(hw->tx_freeq, tx_req, false, 0);
|
|
}
|
|
|
|
static bool crystalhd_tx_list0_handler(struct crystalhd_hw *hw, uint32_t err_sts)
|
|
{
|
|
uint32_t err_mask, tmp;
|
|
unsigned long flags = 0;
|
|
|
|
err_mask = MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_MASK |
|
|
MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_MASK |
|
|
MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK;
|
|
|
|
if (!(err_sts & err_mask))
|
|
return false;
|
|
|
|
BCMLOG_ERR("Error on Tx-L0 %x\n", err_sts);
|
|
|
|
tmp = err_mask;
|
|
|
|
if (err_sts & MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK)
|
|
tmp &= ~MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK;
|
|
|
|
if (tmp) {
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
/* reset list index.*/
|
|
hw->tx_list_post_index = 0;
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
}
|
|
|
|
tmp = err_sts & err_mask;
|
|
crystalhd_reg_wr(hw->adp, MISC1_TX_DMA_ERROR_STATUS, tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool crystalhd_tx_list1_handler(struct crystalhd_hw *hw, uint32_t err_sts)
|
|
{
|
|
uint32_t err_mask, tmp;
|
|
unsigned long flags = 0;
|
|
|
|
err_mask = MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_MASK |
|
|
MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_MASK |
|
|
MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK;
|
|
|
|
if (!(err_sts & err_mask))
|
|
return false;
|
|
|
|
BCMLOG_ERR("Error on Tx-L1 %x\n", err_sts);
|
|
|
|
tmp = err_mask;
|
|
|
|
if (err_sts & MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK)
|
|
tmp &= ~MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK;
|
|
|
|
if (tmp) {
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
/* reset list index.*/
|
|
hw->tx_list_post_index = 0;
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
}
|
|
|
|
tmp = err_sts & err_mask;
|
|
crystalhd_reg_wr(hw->adp, MISC1_TX_DMA_ERROR_STATUS, tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void crystalhd_tx_isr(struct crystalhd_hw *hw, uint32_t int_sts)
|
|
{
|
|
uint32_t err_sts;
|
|
|
|
if (int_sts & INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_MASK)
|
|
crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 0,
|
|
BC_STS_SUCCESS);
|
|
|
|
if (int_sts & INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_MASK)
|
|
crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 1,
|
|
BC_STS_SUCCESS);
|
|
|
|
if (!(int_sts & (INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_MASK |
|
|
INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_MASK))) {
|
|
/* No error mask set.. */
|
|
return;
|
|
}
|
|
|
|
/* Handle Tx errors. */
|
|
err_sts = crystalhd_reg_rd(hw->adp, MISC1_TX_DMA_ERROR_STATUS);
|
|
|
|
if (crystalhd_tx_list0_handler(hw, err_sts))
|
|
crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 0,
|
|
BC_STS_ERROR);
|
|
|
|
if (crystalhd_tx_list1_handler(hw, err_sts))
|
|
crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 1,
|
|
BC_STS_ERROR);
|
|
|
|
hw->stats.tx_errors++;
|
|
}
|
|
|
|
static void crystalhd_hw_dump_desc(struct dma_descriptor *p_dma_desc,
|
|
uint32_t ul_desc_index, uint32_t cnt)
|
|
{
|
|
uint32_t ix, ll = 0;
|
|
|
|
if (!p_dma_desc || !cnt)
|
|
return;
|
|
|
|
/* FIXME: jarod: perhaps a modparam desc_debug to enable this, rather than
|
|
* setting ll (log level, I presume) to non-zero? */
|
|
if (!ll)
|
|
return;
|
|
|
|
for (ix = ul_desc_index; ix < (ul_desc_index + cnt); ix++) {
|
|
BCMLOG(ll, "%s[%d] Buff[%x:%x] Next:[%x:%x] XferSz:%x Intr:%x,Last:%x\n",
|
|
((p_dma_desc[ul_desc_index].dma_dir) ? "TDesc" : "RDesc"),
|
|
ul_desc_index,
|
|
p_dma_desc[ul_desc_index].buff_addr_high,
|
|
p_dma_desc[ul_desc_index].buff_addr_low,
|
|
p_dma_desc[ul_desc_index].next_desc_addr_high,
|
|
p_dma_desc[ul_desc_index].next_desc_addr_low,
|
|
p_dma_desc[ul_desc_index].xfer_size,
|
|
p_dma_desc[ul_desc_index].intr_enable,
|
|
p_dma_desc[ul_desc_index].last_rec_indicator);
|
|
}
|
|
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_hw_fill_desc(struct crystalhd_dio_req *ioreq,
|
|
struct dma_descriptor *desc,
|
|
dma_addr_t desc_paddr_base,
|
|
uint32_t sg_cnt, uint32_t sg_st_ix,
|
|
uint32_t sg_st_off, uint32_t xfr_sz)
|
|
{
|
|
uint32_t count = 0, ix = 0, sg_ix = 0, len = 0, last_desc_ix = 0;
|
|
dma_addr_t desc_phy_addr = desc_paddr_base;
|
|
union addr_64 addr_temp;
|
|
|
|
if (!ioreq || !desc || !desc_paddr_base || !xfr_sz ||
|
|
(!sg_cnt && !ioreq->uinfo.dir_tx)) {
|
|
BCMLOG_ERR("Invalid Args\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
for (ix = 0; ix < sg_cnt; ix++) {
|
|
|
|
/* Setup SGLE index. */
|
|
sg_ix = ix + sg_st_ix;
|
|
|
|
/* Get SGLE length */
|
|
len = crystalhd_get_sgle_len(ioreq, sg_ix);
|
|
if (len % 4) {
|
|
BCMLOG_ERR(" len in sg %d %d %d\n", len, sg_ix, sg_cnt);
|
|
return BC_STS_NOT_IMPL;
|
|
}
|
|
/* Setup DMA desc with Phy addr & Length at current index. */
|
|
addr_temp.full_addr = crystalhd_get_sgle_paddr(ioreq, sg_ix);
|
|
if (sg_ix == sg_st_ix) {
|
|
addr_temp.full_addr += sg_st_off;
|
|
len -= sg_st_off;
|
|
}
|
|
memset(&desc[ix], 0, sizeof(desc[ix]));
|
|
desc[ix].buff_addr_low = addr_temp.low_part;
|
|
desc[ix].buff_addr_high = addr_temp.high_part;
|
|
desc[ix].dma_dir = ioreq->uinfo.dir_tx;
|
|
|
|
/* Chain DMA descriptor. */
|
|
addr_temp.full_addr = desc_phy_addr + sizeof(struct dma_descriptor);
|
|
desc[ix].next_desc_addr_low = addr_temp.low_part;
|
|
desc[ix].next_desc_addr_high = addr_temp.high_part;
|
|
|
|
if ((count + len) > xfr_sz)
|
|
len = xfr_sz - count;
|
|
|
|
/* Debug.. */
|
|
if ((!len) || (len > crystalhd_get_sgle_len(ioreq, sg_ix))) {
|
|
BCMLOG_ERR("inv-len(%x) Ix(%d) count:%x xfr_sz:%x sg_cnt:%d\n",
|
|
len, ix, count, xfr_sz, sg_cnt);
|
|
return BC_STS_ERROR;
|
|
}
|
|
/* Length expects Multiple of 4 */
|
|
desc[ix].xfer_size = (len / 4);
|
|
|
|
crystalhd_hw_dump_desc(desc, ix, 1);
|
|
|
|
count += len;
|
|
desc_phy_addr += sizeof(struct dma_descriptor);
|
|
}
|
|
|
|
last_desc_ix = ix - 1;
|
|
|
|
if (ioreq->fb_size) {
|
|
memset(&desc[ix], 0, sizeof(desc[ix]));
|
|
addr_temp.full_addr = ioreq->fb_pa;
|
|
desc[ix].buff_addr_low = addr_temp.low_part;
|
|
desc[ix].buff_addr_high = addr_temp.high_part;
|
|
desc[ix].dma_dir = ioreq->uinfo.dir_tx;
|
|
desc[ix].xfer_size = 1;
|
|
desc[ix].fill_bytes = 4 - ioreq->fb_size;
|
|
count += ioreq->fb_size;
|
|
last_desc_ix++;
|
|
}
|
|
|
|
/* setup last descriptor..*/
|
|
desc[last_desc_ix].last_rec_indicator = 1;
|
|
desc[last_desc_ix].next_desc_addr_low = 0;
|
|
desc[last_desc_ix].next_desc_addr_high = 0;
|
|
desc[last_desc_ix].intr_enable = 1;
|
|
|
|
crystalhd_hw_dump_desc(desc, last_desc_ix, 1);
|
|
|
|
if (count != xfr_sz) {
|
|
BCMLOG_ERR("interal error sz curr:%x exp:%x\n", count, xfr_sz);
|
|
return BC_STS_ERROR;
|
|
}
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_xlat_sgl_to_dma_desc(struct crystalhd_dio_req *ioreq,
|
|
struct dma_desc_mem *pdesc_mem,
|
|
uint32_t *uv_desc_index)
|
|
{
|
|
struct dma_descriptor *desc = NULL;
|
|
dma_addr_t desc_paddr_base = 0;
|
|
uint32_t sg_cnt = 0, sg_st_ix = 0, sg_st_off = 0;
|
|
uint32_t xfr_sz = 0;
|
|
enum BC_STATUS sts = BC_STS_SUCCESS;
|
|
|
|
/* Check params.. */
|
|
if (!ioreq || !pdesc_mem || !uv_desc_index) {
|
|
BCMLOG_ERR("Invalid Args\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if (!pdesc_mem->sz || !pdesc_mem->pdma_desc_start ||
|
|
!ioreq->sg || (!ioreq->sg_cnt && !ioreq->uinfo.dir_tx)) {
|
|
BCMLOG_ERR("Invalid Args\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if ((ioreq->uinfo.dir_tx) && (ioreq->uinfo.uv_offset)) {
|
|
BCMLOG_ERR("UV offset for TX??\n");
|
|
return BC_STS_INV_ARG;
|
|
|
|
}
|
|
|
|
desc = pdesc_mem->pdma_desc_start;
|
|
desc_paddr_base = pdesc_mem->phy_addr;
|
|
|
|
if (ioreq->uinfo.dir_tx || (ioreq->uinfo.uv_offset == 0)) {
|
|
sg_cnt = ioreq->sg_cnt;
|
|
xfr_sz = ioreq->uinfo.xfr_len;
|
|
} else {
|
|
sg_cnt = ioreq->uinfo.uv_sg_ix + 1;
|
|
xfr_sz = ioreq->uinfo.uv_offset;
|
|
}
|
|
|
|
sts = crystalhd_hw_fill_desc(ioreq, desc, desc_paddr_base, sg_cnt,
|
|
sg_st_ix, sg_st_off, xfr_sz);
|
|
|
|
if ((sts != BC_STS_SUCCESS) || !ioreq->uinfo.uv_offset)
|
|
return sts;
|
|
|
|
/* Prepare for UV mapping.. */
|
|
desc = &pdesc_mem->pdma_desc_start[sg_cnt];
|
|
desc_paddr_base = pdesc_mem->phy_addr +
|
|
(sg_cnt * sizeof(struct dma_descriptor));
|
|
|
|
/* Done with desc addr.. now update sg stuff.*/
|
|
sg_cnt = ioreq->sg_cnt - ioreq->uinfo.uv_sg_ix;
|
|
xfr_sz = ioreq->uinfo.xfr_len - ioreq->uinfo.uv_offset;
|
|
sg_st_ix = ioreq->uinfo.uv_sg_ix;
|
|
sg_st_off = ioreq->uinfo.uv_sg_off;
|
|
|
|
sts = crystalhd_hw_fill_desc(ioreq, desc, desc_paddr_base, sg_cnt,
|
|
sg_st_ix, sg_st_off, xfr_sz);
|
|
if (sts != BC_STS_SUCCESS)
|
|
return sts;
|
|
|
|
*uv_desc_index = sg_st_ix;
|
|
|
|
return sts;
|
|
}
|
|
|
|
static void crystalhd_start_tx_dma_engine(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t dma_cntrl;
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS);
|
|
if (!(dma_cntrl & DMA_START_BIT)) {
|
|
dma_cntrl |= DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS,
|
|
dma_cntrl);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* _CHECK_THIS_
|
|
*
|
|
* Verify if the Stop generates a completion interrupt or not.
|
|
* if it does not generate an interrupt, then add polling here.
|
|
*/
|
|
static enum BC_STATUS crystalhd_stop_tx_dma_engine(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t dma_cntrl, cnt = 30;
|
|
uint32_t l1 = 1, l2 = 1;
|
|
unsigned long flags = 0;
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS);
|
|
|
|
BCMLOG(BCMLOG_DBG, "Stopping TX DMA Engine..\n");
|
|
|
|
/* FIXME: jarod: invert dma_ctrl and check bit? or are there missing parens? */
|
|
if (!dma_cntrl & DMA_START_BIT) {
|
|
BCMLOG(BCMLOG_DBG, "Already Stopped\n");
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
crystalhd_disable_interrupts(hw->adp);
|
|
|
|
/* Issue stop to HW */
|
|
/* This bit when set gave problems. Please check*/
|
|
dma_cntrl &= ~DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
|
|
BCMLOG(BCMLOG_DBG, "Cleared the DMA Start bit\n");
|
|
|
|
/* Poll for 3seconds (30 * 100ms) on both the lists..*/
|
|
while ((l1 || l2) && cnt) {
|
|
|
|
if (l1) {
|
|
l1 = crystalhd_reg_rd(hw->adp, MISC1_TX_FIRST_DESC_L_ADDR_LIST0);
|
|
l1 &= DMA_START_BIT;
|
|
}
|
|
|
|
if (l2) {
|
|
l2 = crystalhd_reg_rd(hw->adp, MISC1_TX_FIRST_DESC_L_ADDR_LIST1);
|
|
l2 &= DMA_START_BIT;
|
|
}
|
|
|
|
msleep_interruptible(100);
|
|
|
|
cnt--;
|
|
}
|
|
|
|
if (!cnt) {
|
|
BCMLOG_ERR("Failed to stop TX DMA.. l1 %d, l2 %d\n", l1, l2);
|
|
crystalhd_enable_interrupts(hw->adp);
|
|
return BC_STS_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
hw->tx_list_post_index = 0;
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
BCMLOG(BCMLOG_DBG, "stopped TX DMA..\n");
|
|
crystalhd_enable_interrupts(hw->adp);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
static uint32_t crystalhd_get_pib_avail_cnt(struct crystalhd_hw *hw)
|
|
{
|
|
/*
|
|
* Position of the PIB Entries can be found at
|
|
* 0th and the 1st location of the Circular list.
|
|
*/
|
|
uint32_t Q_addr;
|
|
uint32_t pib_cnt, r_offset, w_offset;
|
|
|
|
Q_addr = hw->pib_del_Q_addr;
|
|
|
|
/* Get the Read Pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
|
|
|
|
/* Get the Write Pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
|
|
|
|
if (r_offset == w_offset)
|
|
return 0; /* Queue is empty */
|
|
|
|
if (w_offset > r_offset)
|
|
pib_cnt = w_offset - r_offset;
|
|
else
|
|
pib_cnt = (w_offset + MAX_PIB_Q_DEPTH) -
|
|
(r_offset + MIN_PIB_Q_DEPTH);
|
|
|
|
if (pib_cnt > MAX_PIB_Q_DEPTH) {
|
|
BCMLOG_ERR("Invalid PIB Count (%u)\n", pib_cnt);
|
|
return 0;
|
|
}
|
|
|
|
return pib_cnt;
|
|
}
|
|
|
|
static uint32_t crystalhd_get_addr_from_pib_Q(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t Q_addr;
|
|
uint32_t addr_entry, r_offset, w_offset;
|
|
|
|
Q_addr = hw->pib_del_Q_addr;
|
|
|
|
/* Get the Read Pointer 0Th Location is Read Pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
|
|
|
|
/* Get the Write Pointer 1st Location is Write pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
|
|
|
|
/* Queue is empty */
|
|
if (r_offset == w_offset)
|
|
return 0;
|
|
|
|
if ((r_offset < MIN_PIB_Q_DEPTH) || (r_offset >= MAX_PIB_Q_DEPTH))
|
|
return 0;
|
|
|
|
/* Get the Actual Address of the PIB */
|
|
crystalhd_mem_rd(hw->adp, Q_addr + (r_offset * sizeof(uint32_t)),
|
|
1, &addr_entry);
|
|
|
|
/* Increment the Read Pointer */
|
|
r_offset++;
|
|
|
|
if (MAX_PIB_Q_DEPTH == r_offset)
|
|
r_offset = MIN_PIB_Q_DEPTH;
|
|
|
|
/* Write back the read pointer to It's Location */
|
|
crystalhd_mem_wr(hw->adp, Q_addr, 1, &r_offset);
|
|
|
|
return addr_entry;
|
|
}
|
|
|
|
static bool crystalhd_rel_addr_to_pib_Q(struct crystalhd_hw *hw, uint32_t addr_to_rel)
|
|
{
|
|
uint32_t Q_addr;
|
|
uint32_t r_offset, w_offset, n_offset;
|
|
|
|
Q_addr = hw->pib_rel_Q_addr;
|
|
|
|
/* Get the Read Pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
|
|
|
|
/* Get the Write Pointer */
|
|
crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
|
|
|
|
if ((r_offset < MIN_PIB_Q_DEPTH) ||
|
|
(r_offset >= MAX_PIB_Q_DEPTH))
|
|
return false;
|
|
|
|
n_offset = w_offset + 1;
|
|
|
|
if (MAX_PIB_Q_DEPTH == n_offset)
|
|
n_offset = MIN_PIB_Q_DEPTH;
|
|
|
|
if (r_offset == n_offset)
|
|
return false; /* should never happen */
|
|
|
|
/* Write the DRAM ADDR to the Queue at Next Offset */
|
|
crystalhd_mem_wr(hw->adp, Q_addr + (w_offset * sizeof(uint32_t)),
|
|
1, &addr_to_rel);
|
|
|
|
/* Put the New value of the write pointer in Queue */
|
|
crystalhd_mem_wr(hw->adp, Q_addr + sizeof(uint32_t), 1, &n_offset);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cpy_pib_to_app(struct c011_pib *src_pib, struct BC_PIC_INFO_BLOCK *dst_pib)
|
|
{
|
|
if (!src_pib || !dst_pib) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return;
|
|
}
|
|
|
|
dst_pib->timeStamp = 0;
|
|
dst_pib->picture_number = src_pib->ppb.picture_number;
|
|
dst_pib->width = src_pib->ppb.width;
|
|
dst_pib->height = src_pib->ppb.height;
|
|
dst_pib->chroma_format = src_pib->ppb.chroma_format;
|
|
dst_pib->pulldown = src_pib->ppb.pulldown;
|
|
dst_pib->flags = src_pib->ppb.flags;
|
|
dst_pib->sess_num = src_pib->ptsStcOffset;
|
|
dst_pib->aspect_ratio = src_pib->ppb.aspect_ratio;
|
|
dst_pib->colour_primaries = src_pib->ppb.colour_primaries;
|
|
dst_pib->picture_meta_payload = src_pib->ppb.picture_meta_payload;
|
|
dst_pib->frame_rate = src_pib->resolution ;
|
|
return;
|
|
}
|
|
|
|
static void crystalhd_hw_proc_pib(struct crystalhd_hw *hw)
|
|
{
|
|
unsigned int cnt;
|
|
struct c011_pib src_pib;
|
|
uint32_t pib_addr, pib_cnt;
|
|
struct BC_PIC_INFO_BLOCK *AppPib;
|
|
struct crystalhd_rx_dma_pkt *rx_pkt = NULL;
|
|
|
|
pib_cnt = crystalhd_get_pib_avail_cnt(hw);
|
|
|
|
if (!pib_cnt)
|
|
return;
|
|
|
|
for (cnt = 0; cnt < pib_cnt; cnt++) {
|
|
|
|
pib_addr = crystalhd_get_addr_from_pib_Q(hw);
|
|
crystalhd_mem_rd(hw->adp, pib_addr, sizeof(struct c011_pib) / 4,
|
|
(uint32_t *)&src_pib);
|
|
|
|
if (src_pib.bFormatChange) {
|
|
rx_pkt = (struct crystalhd_rx_dma_pkt *)crystalhd_dioq_fetch(hw->rx_freeq);
|
|
if (!rx_pkt)
|
|
return;
|
|
rx_pkt->flags = 0;
|
|
rx_pkt->flags |= COMP_FLAG_PIB_VALID | COMP_FLAG_FMT_CHANGE;
|
|
AppPib = &rx_pkt->pib;
|
|
cpy_pib_to_app(&src_pib, AppPib);
|
|
|
|
BCMLOG(BCMLOG_DBG,
|
|
"App PIB:%x %x %x %x %x %x %x %x %x %x\n",
|
|
rx_pkt->pib.picture_number,
|
|
rx_pkt->pib.aspect_ratio,
|
|
rx_pkt->pib.chroma_format,
|
|
rx_pkt->pib.colour_primaries,
|
|
rx_pkt->pib.frame_rate,
|
|
rx_pkt->pib.height,
|
|
rx_pkt->pib.height,
|
|
rx_pkt->pib.n_drop,
|
|
rx_pkt->pib.pulldown,
|
|
rx_pkt->pib.ycom);
|
|
|
|
crystalhd_dioq_add(hw->rx_rdyq, (void *)rx_pkt, true, rx_pkt->pkt_tag);
|
|
|
|
}
|
|
|
|
crystalhd_rel_addr_to_pib_Q(hw, pib_addr);
|
|
}
|
|
}
|
|
|
|
static void crystalhd_start_rx_dma_engine(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t dma_cntrl;
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
|
|
if (!(dma_cntrl & DMA_START_BIT)) {
|
|
dma_cntrl |= DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
|
|
if (!(dma_cntrl & DMA_START_BIT)) {
|
|
dma_cntrl |= DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void crystalhd_stop_rx_dma_engine(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t dma_cntrl = 0, count = 30;
|
|
uint32_t l0y = 1, l0uv = 1, l1y = 1, l1uv = 1;
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
|
|
if ((dma_cntrl & DMA_START_BIT)) {
|
|
dma_cntrl &= ~DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
|
|
if ((dma_cntrl & DMA_START_BIT)) {
|
|
dma_cntrl &= ~DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
|
|
/* Poll for 3seconds (30 * 100ms) on both the lists..*/
|
|
while ((l0y || l0uv || l1y || l1uv) && count) {
|
|
|
|
if (l0y) {
|
|
l0y = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST0);
|
|
l0y &= DMA_START_BIT;
|
|
if (!l0y)
|
|
hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
|
|
}
|
|
|
|
if (l1y) {
|
|
l1y = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST1);
|
|
l1y &= DMA_START_BIT;
|
|
if (!l1y)
|
|
hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
|
|
}
|
|
|
|
if (l0uv) {
|
|
l0uv = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST0);
|
|
l0uv &= DMA_START_BIT;
|
|
if (!l0uv)
|
|
hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
|
|
}
|
|
|
|
if (l1uv) {
|
|
l1uv = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST1);
|
|
l1uv &= DMA_START_BIT;
|
|
if (!l1uv)
|
|
hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
|
|
}
|
|
msleep_interruptible(100);
|
|
count--;
|
|
}
|
|
|
|
hw->rx_list_post_index = 0;
|
|
|
|
BCMLOG(BCMLOG_SSTEP, "Capture Stop: %d List0:Sts:%x List1:Sts:%x\n",
|
|
count, hw->rx_list_sts[0], hw->rx_list_sts[1]);
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_hw_prog_rxdma(struct crystalhd_hw *hw, struct crystalhd_rx_dma_pkt *rx_pkt)
|
|
{
|
|
uint32_t y_low_addr_reg, y_high_addr_reg;
|
|
uint32_t uv_low_addr_reg, uv_high_addr_reg;
|
|
union addr_64 desc_addr;
|
|
unsigned long flags;
|
|
|
|
if (!hw || !rx_pkt) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if (hw->rx_list_post_index >= DMA_ENGINE_CNT) {
|
|
BCMLOG_ERR("List Out Of bounds %x\n", hw->rx_list_post_index);
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
spin_lock_irqsave(&hw->rx_lock, flags);
|
|
/* FIXME: jarod: sts_free is an enum for 0, in crystalhd_hw.h... yuk... */
|
|
if (sts_free != hw->rx_list_sts[hw->rx_list_post_index]) {
|
|
spin_unlock_irqrestore(&hw->rx_lock, flags);
|
|
return BC_STS_BUSY;
|
|
}
|
|
|
|
if (!hw->rx_list_post_index) {
|
|
y_low_addr_reg = MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST0;
|
|
y_high_addr_reg = MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST0;
|
|
uv_low_addr_reg = MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST0;
|
|
uv_high_addr_reg = MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST0;
|
|
} else {
|
|
y_low_addr_reg = MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST1;
|
|
y_high_addr_reg = MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST1;
|
|
uv_low_addr_reg = MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST1;
|
|
uv_high_addr_reg = MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST1;
|
|
}
|
|
rx_pkt->pkt_tag = hw->rx_pkt_tag_seed + hw->rx_list_post_index;
|
|
hw->rx_list_sts[hw->rx_list_post_index] |= rx_waiting_y_intr;
|
|
if (rx_pkt->uv_phy_addr)
|
|
hw->rx_list_sts[hw->rx_list_post_index] |= rx_waiting_uv_intr;
|
|
hw->rx_list_post_index = (hw->rx_list_post_index + 1) % DMA_ENGINE_CNT;
|
|
spin_unlock_irqrestore(&hw->rx_lock, flags);
|
|
|
|
crystalhd_dioq_add(hw->rx_actq, (void *)rx_pkt, false, rx_pkt->pkt_tag);
|
|
|
|
crystalhd_start_rx_dma_engine(hw);
|
|
/* Program the Y descriptor */
|
|
desc_addr.full_addr = rx_pkt->desc_mem.phy_addr;
|
|
crystalhd_reg_wr(hw->adp, y_high_addr_reg, desc_addr.high_part);
|
|
crystalhd_reg_wr(hw->adp, y_low_addr_reg, desc_addr.low_part | 0x01);
|
|
|
|
if (rx_pkt->uv_phy_addr) {
|
|
/* Program the UV descriptor */
|
|
desc_addr.full_addr = rx_pkt->uv_phy_addr;
|
|
crystalhd_reg_wr(hw->adp, uv_high_addr_reg, desc_addr.high_part);
|
|
crystalhd_reg_wr(hw->adp, uv_low_addr_reg, desc_addr.low_part | 0x01);
|
|
}
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_hw_post_cap_buff(struct crystalhd_hw *hw,
|
|
struct crystalhd_rx_dma_pkt *rx_pkt)
|
|
{
|
|
enum BC_STATUS sts = crystalhd_hw_prog_rxdma(hw, rx_pkt);
|
|
|
|
if (sts == BC_STS_BUSY)
|
|
crystalhd_dioq_add(hw->rx_freeq, (void *)rx_pkt,
|
|
false, rx_pkt->pkt_tag);
|
|
|
|
return sts;
|
|
}
|
|
|
|
static void crystalhd_get_dnsz(struct crystalhd_hw *hw, uint32_t list_index,
|
|
uint32_t *y_dw_dnsz, uint32_t *uv_dw_dnsz)
|
|
{
|
|
uint32_t y_dn_sz_reg, uv_dn_sz_reg;
|
|
|
|
if (!list_index) {
|
|
y_dn_sz_reg = MISC1_Y_RX_LIST0_CUR_BYTE_CNT;
|
|
uv_dn_sz_reg = MISC1_UV_RX_LIST0_CUR_BYTE_CNT;
|
|
} else {
|
|
y_dn_sz_reg = MISC1_Y_RX_LIST1_CUR_BYTE_CNT;
|
|
uv_dn_sz_reg = MISC1_UV_RX_LIST1_CUR_BYTE_CNT;
|
|
}
|
|
|
|
*y_dw_dnsz = crystalhd_reg_rd(hw->adp, y_dn_sz_reg);
|
|
*uv_dw_dnsz = crystalhd_reg_rd(hw->adp, uv_dn_sz_reg);
|
|
}
|
|
|
|
/*
|
|
* This function should be called only after making sure that the two DMA
|
|
* lists are free. This function does not check if DMA's are active, before
|
|
* turning off the DMA.
|
|
*/
|
|
static void crystalhd_hw_finalize_pause(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t dma_cntrl, aspm;
|
|
|
|
hw->stop_pending = 0;
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
|
|
if (dma_cntrl & DMA_START_BIT) {
|
|
dma_cntrl &= ~DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
|
|
dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
|
|
if (dma_cntrl & DMA_START_BIT) {
|
|
dma_cntrl &= ~DMA_START_BIT;
|
|
crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
|
|
}
|
|
hw->rx_list_post_index = 0;
|
|
|
|
aspm = crystalhd_reg_rd(hw->adp, PCIE_DLL_DATA_LINK_CONTROL);
|
|
aspm |= ASPM_L1_ENABLE;
|
|
/* NAREN BCMLOG(BCMLOG_INFO, "aspm on\n"); */
|
|
crystalhd_reg_wr(hw->adp, PCIE_DLL_DATA_LINK_CONTROL, aspm);
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_rx_pkt_done(struct crystalhd_hw *hw, uint32_t list_index,
|
|
enum BC_STATUS comp_sts)
|
|
{
|
|
struct crystalhd_rx_dma_pkt *rx_pkt = NULL;
|
|
uint32_t y_dw_dnsz, uv_dw_dnsz;
|
|
enum BC_STATUS sts = BC_STS_SUCCESS;
|
|
|
|
if (!hw || list_index >= DMA_ENGINE_CNT) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
rx_pkt = crystalhd_dioq_find_and_fetch(hw->rx_actq,
|
|
hw->rx_pkt_tag_seed + list_index);
|
|
if (!rx_pkt) {
|
|
BCMLOG_ERR("Act-Q:PostIx:%x L0Sts:%x L1Sts:%x current L:%x tag:%x comp:%x\n",
|
|
hw->rx_list_post_index, hw->rx_list_sts[0],
|
|
hw->rx_list_sts[1], list_index,
|
|
hw->rx_pkt_tag_seed + list_index, comp_sts);
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if (comp_sts == BC_STS_SUCCESS) {
|
|
crystalhd_get_dnsz(hw, list_index, &y_dw_dnsz, &uv_dw_dnsz);
|
|
rx_pkt->dio_req->uinfo.y_done_sz = y_dw_dnsz;
|
|
rx_pkt->flags = COMP_FLAG_DATA_VALID;
|
|
if (rx_pkt->uv_phy_addr)
|
|
rx_pkt->dio_req->uinfo.uv_done_sz = uv_dw_dnsz;
|
|
crystalhd_dioq_add(hw->rx_rdyq, rx_pkt, true,
|
|
hw->rx_pkt_tag_seed + list_index);
|
|
return sts;
|
|
}
|
|
|
|
/* Check if we can post this DIO again. */
|
|
return crystalhd_hw_post_cap_buff(hw, rx_pkt);
|
|
}
|
|
|
|
static bool crystalhd_rx_list0_handler(struct crystalhd_hw *hw, uint32_t int_sts,
|
|
uint32_t y_err_sts, uint32_t uv_err_sts)
|
|
{
|
|
uint32_t tmp;
|
|
enum list_sts tmp_lsts;
|
|
|
|
if (!(y_err_sts & GET_Y0_ERR_MSK) && !(uv_err_sts & GET_UV0_ERR_MSK))
|
|
return false;
|
|
|
|
tmp_lsts = hw->rx_list_sts[0];
|
|
|
|
/* Y0 - DMA */
|
|
tmp = y_err_sts & GET_Y0_ERR_MSK;
|
|
if (int_sts & INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_MASK)
|
|
hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
|
|
|
|
if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK) {
|
|
hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
|
|
tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK;
|
|
}
|
|
|
|
if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK) {
|
|
hw->rx_list_sts[0] &= ~rx_y_mask;
|
|
hw->rx_list_sts[0] |= rx_y_error;
|
|
tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK;
|
|
}
|
|
|
|
if (tmp) {
|
|
hw->rx_list_sts[0] &= ~rx_y_mask;
|
|
hw->rx_list_sts[0] |= rx_y_error;
|
|
hw->rx_list_post_index = 0;
|
|
}
|
|
|
|
/* UV0 - DMA */
|
|
tmp = uv_err_sts & GET_UV0_ERR_MSK;
|
|
if (int_sts & INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_MASK)
|
|
hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
|
|
|
|
if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK) {
|
|
hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
|
|
tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK;
|
|
}
|
|
|
|
if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK) {
|
|
hw->rx_list_sts[0] &= ~rx_uv_mask;
|
|
hw->rx_list_sts[0] |= rx_uv_error;
|
|
tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK;
|
|
}
|
|
|
|
if (tmp) {
|
|
hw->rx_list_sts[0] &= ~rx_uv_mask;
|
|
hw->rx_list_sts[0] |= rx_uv_error;
|
|
hw->rx_list_post_index = 0;
|
|
}
|
|
|
|
if (y_err_sts & GET_Y0_ERR_MSK) {
|
|
tmp = y_err_sts & GET_Y0_ERR_MSK;
|
|
crystalhd_reg_wr(hw->adp, MISC1_Y_RX_ERROR_STATUS, tmp);
|
|
}
|
|
|
|
if (uv_err_sts & GET_UV0_ERR_MSK) {
|
|
tmp = uv_err_sts & GET_UV0_ERR_MSK;
|
|
crystalhd_reg_wr(hw->adp, MISC1_UV_RX_ERROR_STATUS, tmp);
|
|
}
|
|
|
|
return (tmp_lsts != hw->rx_list_sts[0]);
|
|
}
|
|
|
|
static bool crystalhd_rx_list1_handler(struct crystalhd_hw *hw, uint32_t int_sts,
|
|
uint32_t y_err_sts, uint32_t uv_err_sts)
|
|
{
|
|
uint32_t tmp;
|
|
enum list_sts tmp_lsts;
|
|
|
|
if (!(y_err_sts & GET_Y1_ERR_MSK) && !(uv_err_sts & GET_UV1_ERR_MSK))
|
|
return false;
|
|
|
|
tmp_lsts = hw->rx_list_sts[1];
|
|
|
|
/* Y1 - DMA */
|
|
tmp = y_err_sts & GET_Y1_ERR_MSK;
|
|
if (int_sts & INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_MASK)
|
|
hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
|
|
|
|
if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK) {
|
|
hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
|
|
tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK;
|
|
}
|
|
|
|
if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK) {
|
|
/* Add retry-support..*/
|
|
hw->rx_list_sts[1] &= ~rx_y_mask;
|
|
hw->rx_list_sts[1] |= rx_y_error;
|
|
tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK;
|
|
}
|
|
|
|
if (tmp) {
|
|
hw->rx_list_sts[1] &= ~rx_y_mask;
|
|
hw->rx_list_sts[1] |= rx_y_error;
|
|
hw->rx_list_post_index = 0;
|
|
}
|
|
|
|
/* UV1 - DMA */
|
|
tmp = uv_err_sts & GET_UV1_ERR_MSK;
|
|
if (int_sts & INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_MASK)
|
|
hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
|
|
|
|
if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK) {
|
|
hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
|
|
tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK;
|
|
}
|
|
|
|
if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK) {
|
|
/* Add retry-support*/
|
|
hw->rx_list_sts[1] &= ~rx_uv_mask;
|
|
hw->rx_list_sts[1] |= rx_uv_error;
|
|
tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK;
|
|
}
|
|
|
|
if (tmp) {
|
|
hw->rx_list_sts[1] &= ~rx_uv_mask;
|
|
hw->rx_list_sts[1] |= rx_uv_error;
|
|
hw->rx_list_post_index = 0;
|
|
}
|
|
|
|
if (y_err_sts & GET_Y1_ERR_MSK) {
|
|
tmp = y_err_sts & GET_Y1_ERR_MSK;
|
|
crystalhd_reg_wr(hw->adp, MISC1_Y_RX_ERROR_STATUS, tmp);
|
|
}
|
|
|
|
if (uv_err_sts & GET_UV1_ERR_MSK) {
|
|
tmp = uv_err_sts & GET_UV1_ERR_MSK;
|
|
crystalhd_reg_wr(hw->adp, MISC1_UV_RX_ERROR_STATUS, tmp);
|
|
}
|
|
|
|
return (tmp_lsts != hw->rx_list_sts[1]);
|
|
}
|
|
|
|
|
|
static void crystalhd_rx_isr(struct crystalhd_hw *hw, uint32_t intr_sts)
|
|
{
|
|
unsigned long flags;
|
|
uint32_t i, list_avail = 0;
|
|
enum BC_STATUS comp_sts = BC_STS_NO_DATA;
|
|
uint32_t y_err_sts, uv_err_sts, y_dn_sz = 0, uv_dn_sz = 0;
|
|
bool ret = 0;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return;
|
|
}
|
|
|
|
if (!(intr_sts & GET_RX_INTR_MASK))
|
|
return;
|
|
|
|
y_err_sts = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_ERROR_STATUS);
|
|
uv_err_sts = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_ERROR_STATUS);
|
|
|
|
for (i = 0; i < DMA_ENGINE_CNT; i++) {
|
|
/* Update States..*/
|
|
spin_lock_irqsave(&hw->rx_lock, flags);
|
|
if (i == 0)
|
|
ret = crystalhd_rx_list0_handler(hw, intr_sts, y_err_sts, uv_err_sts);
|
|
else
|
|
ret = crystalhd_rx_list1_handler(hw, intr_sts, y_err_sts, uv_err_sts);
|
|
if (ret) {
|
|
switch (hw->rx_list_sts[i]) {
|
|
case sts_free:
|
|
comp_sts = BC_STS_SUCCESS;
|
|
list_avail = 1;
|
|
break;
|
|
case rx_y_error:
|
|
case rx_uv_error:
|
|
case rx_sts_error:
|
|
/* We got error on both or Y or uv. */
|
|
hw->stats.rx_errors++;
|
|
crystalhd_get_dnsz(hw, i, &y_dn_sz, &uv_dn_sz);
|
|
/* FIXME: jarod: this is where my mini pci-e card is tripping up */
|
|
BCMLOG(BCMLOG_DBG, "list_index:%x rx[%d] Y:%x "
|
|
"UV:%x Int:%x YDnSz:%x UVDnSz:%x\n",
|
|
i, hw->stats.rx_errors, y_err_sts,
|
|
uv_err_sts, intr_sts, y_dn_sz, uv_dn_sz);
|
|
hw->rx_list_sts[i] = sts_free;
|
|
comp_sts = BC_STS_ERROR;
|
|
break;
|
|
default:
|
|
/* Wait for completion..*/
|
|
comp_sts = BC_STS_NO_DATA;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&hw->rx_lock, flags);
|
|
|
|
/* handle completion...*/
|
|
if (comp_sts != BC_STS_NO_DATA) {
|
|
crystalhd_rx_pkt_done(hw, i, comp_sts);
|
|
comp_sts = BC_STS_NO_DATA;
|
|
}
|
|
}
|
|
|
|
if (list_avail) {
|
|
if (hw->stop_pending) {
|
|
if ((hw->rx_list_sts[0] == sts_free) &&
|
|
(hw->rx_list_sts[1] == sts_free))
|
|
crystalhd_hw_finalize_pause(hw);
|
|
} else {
|
|
crystalhd_hw_start_capture(hw);
|
|
}
|
|
}
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_fw_cmd_post_proc(struct crystalhd_hw *hw,
|
|
struct BC_FW_CMD *fw_cmd)
|
|
{
|
|
enum BC_STATUS sts = BC_STS_SUCCESS;
|
|
struct dec_rsp_channel_start_video *st_rsp = NULL;
|
|
|
|
switch (fw_cmd->cmd[0]) {
|
|
case eCMD_C011_DEC_CHAN_START_VIDEO:
|
|
st_rsp = (struct dec_rsp_channel_start_video *)fw_cmd->rsp;
|
|
hw->pib_del_Q_addr = st_rsp->picInfoDeliveryQ;
|
|
hw->pib_rel_Q_addr = st_rsp->picInfoReleaseQ;
|
|
BCMLOG(BCMLOG_DBG, "DelQAddr:%x RelQAddr:%x\n",
|
|
hw->pib_del_Q_addr, hw->pib_rel_Q_addr);
|
|
break;
|
|
case eCMD_C011_INIT:
|
|
if (!(crystalhd_load_firmware_config(hw->adp))) {
|
|
BCMLOG_ERR("Invalid Params.\n");
|
|
sts = BC_STS_FW_AUTH_FAILED;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
static enum BC_STATUS crystalhd_put_ddr2sleep(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t reg;
|
|
union link_misc_perst_decoder_ctrl rst_cntrl_reg;
|
|
|
|
/* Pulse reset pin of 7412 (MISC_PERST_DECODER_CTRL) */
|
|
rst_cntrl_reg.whole_reg = crystalhd_reg_rd(hw->adp, MISC_PERST_DECODER_CTRL);
|
|
|
|
rst_cntrl_reg.bcm_7412_rst = 1;
|
|
crystalhd_reg_wr(hw->adp, MISC_PERST_DECODER_CTRL, rst_cntrl_reg.whole_reg);
|
|
msleep_interruptible(50);
|
|
|
|
rst_cntrl_reg.bcm_7412_rst = 0;
|
|
crystalhd_reg_wr(hw->adp, MISC_PERST_DECODER_CTRL, rst_cntrl_reg.whole_reg);
|
|
|
|
/* Close all banks, put DDR in idle */
|
|
bc_dec_reg_wr(hw->adp, SDRAM_PRECHARGE, 0);
|
|
|
|
/* Set bit 25 (drop CKE pin of DDR) */
|
|
reg = bc_dec_reg_rd(hw->adp, SDRAM_PARAM);
|
|
reg |= 0x02000000;
|
|
bc_dec_reg_wr(hw->adp, SDRAM_PARAM, reg);
|
|
|
|
/* Reset the audio block */
|
|
bc_dec_reg_wr(hw->adp, AUD_DSP_MISC_SOFT_RESET, 0x1);
|
|
|
|
/* Power down Raptor PLL */
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllCCtl);
|
|
reg |= 0x00008000;
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllCCtl, reg);
|
|
|
|
/* Power down all Audio PLL */
|
|
bc_dec_reg_wr(hw->adp, AIO_MISC_PLL_RESET, 0x1);
|
|
|
|
/* Power down video clock (75MHz) */
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllECtl);
|
|
reg |= 0x00008000;
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllECtl, reg);
|
|
|
|
/* Power down video clock (75MHz) */
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllDCtl);
|
|
reg |= 0x00008000;
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllDCtl, reg);
|
|
|
|
/* Power down core clock (200MHz) */
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
|
|
reg |= 0x00008000;
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllACtl, reg);
|
|
|
|
/* Power down core clock (200MHz) */
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllBCtl);
|
|
reg |= 0x00008000;
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllBCtl, reg);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
/************************************************
|
|
**
|
|
*************************************************/
|
|
|
|
enum BC_STATUS crystalhd_download_fw(struct crystalhd_adp *adp, void *buffer, uint32_t sz)
|
|
{
|
|
uint32_t reg_data, cnt, *temp_buff;
|
|
uint32_t fw_sig_len = 36;
|
|
uint32_t dram_offset = BC_FWIMG_ST_ADDR, sig_reg;
|
|
|
|
BCMLOG_ENTER;
|
|
|
|
if (!adp || !buffer || !sz) {
|
|
BCMLOG_ERR("Invalid Params.\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
reg_data = crystalhd_reg_rd(adp, OTP_CMD);
|
|
if (!(reg_data & 0x02)) {
|
|
BCMLOG_ERR("Invalid hw config.. otp not programmed\n");
|
|
return BC_STS_ERROR;
|
|
}
|
|
|
|
reg_data = 0;
|
|
crystalhd_reg_wr(adp, DCI_CMD, 0);
|
|
reg_data |= BC_BIT(0);
|
|
crystalhd_reg_wr(adp, DCI_CMD, reg_data);
|
|
|
|
reg_data = 0;
|
|
cnt = 1000;
|
|
msleep_interruptible(10);
|
|
|
|
while (reg_data != BC_BIT(4)) {
|
|
reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
|
|
reg_data &= BC_BIT(4);
|
|
if (--cnt == 0) {
|
|
BCMLOG_ERR("Firmware Download RDY Timeout.\n");
|
|
return BC_STS_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
msleep_interruptible(10);
|
|
/* Load the FW to the FW_ADDR field in the DCI_FIRMWARE_ADDR */
|
|
crystalhd_reg_wr(adp, DCI_FIRMWARE_ADDR, dram_offset);
|
|
temp_buff = (uint32_t *)buffer;
|
|
for (cnt = 0; cnt < (sz - fw_sig_len); cnt += 4) {
|
|
crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (dram_offset >> 19));
|
|
crystalhd_reg_wr(adp, DCI_FIRMWARE_DATA, *temp_buff);
|
|
dram_offset += 4;
|
|
temp_buff++;
|
|
}
|
|
msleep_interruptible(10);
|
|
|
|
temp_buff++;
|
|
|
|
sig_reg = (uint32_t)DCI_SIGNATURE_DATA_7;
|
|
for (cnt = 0; cnt < 8; cnt++) {
|
|
uint32_t swapped_data = *temp_buff;
|
|
swapped_data = bswap_32_1(swapped_data);
|
|
crystalhd_reg_wr(adp, sig_reg, swapped_data);
|
|
sig_reg -= 4;
|
|
temp_buff++;
|
|
}
|
|
msleep_interruptible(10);
|
|
|
|
reg_data = 0;
|
|
reg_data |= BC_BIT(1);
|
|
crystalhd_reg_wr(adp, DCI_CMD, reg_data);
|
|
msleep_interruptible(10);
|
|
|
|
reg_data = 0;
|
|
reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
|
|
|
|
if ((reg_data & BC_BIT(9)) == BC_BIT(9)) {
|
|
cnt = 1000;
|
|
while ((reg_data & BC_BIT(0)) != BC_BIT(0)) {
|
|
reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
|
|
reg_data &= BC_BIT(0);
|
|
if (!(--cnt))
|
|
break;
|
|
msleep_interruptible(10);
|
|
}
|
|
reg_data = 0;
|
|
reg_data = crystalhd_reg_rd(adp, DCI_CMD);
|
|
reg_data |= BC_BIT(4);
|
|
crystalhd_reg_wr(adp, DCI_CMD, reg_data);
|
|
|
|
} else {
|
|
BCMLOG_ERR("F/w Signature mismatch\n");
|
|
return BC_STS_FW_AUTH_FAILED;
|
|
}
|
|
|
|
BCMLOG(BCMLOG_INFO, "Firmware Downloaded Successfully\n");
|
|
return BC_STS_SUCCESS;;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw,
|
|
struct BC_FW_CMD *fw_cmd)
|
|
{
|
|
uint32_t cnt = 0, cmd_res_addr;
|
|
uint32_t *cmd_buff, *res_buff;
|
|
wait_queue_head_t fw_cmd_event;
|
|
int rc = 0;
|
|
enum BC_STATUS sts;
|
|
|
|
crystalhd_create_event(&fw_cmd_event);
|
|
|
|
BCMLOG_ENTER;
|
|
|
|
if (!hw || !fw_cmd) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
cmd_buff = fw_cmd->cmd;
|
|
res_buff = fw_cmd->rsp;
|
|
|
|
if (!cmd_buff || !res_buff) {
|
|
BCMLOG_ERR("Invalid Parameters for F/W Command\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
hw->pwr_lock++;
|
|
|
|
hw->fwcmd_evt_sts = 0;
|
|
hw->pfw_cmd_event = &fw_cmd_event;
|
|
|
|
/*Write the command to the memory*/
|
|
crystalhd_mem_wr(hw->adp, TS_Host2CpuSnd, FW_CMD_BUFF_SZ, cmd_buff);
|
|
|
|
/*Memory Read for memory arbitrator flush*/
|
|
crystalhd_mem_rd(hw->adp, TS_Host2CpuSnd, 1, &cnt);
|
|
|
|
/* Write the command address to mailbox */
|
|
bc_dec_reg_wr(hw->adp, Hst2CpuMbx1, TS_Host2CpuSnd);
|
|
msleep_interruptible(50);
|
|
|
|
crystalhd_wait_on_event(&fw_cmd_event, hw->fwcmd_evt_sts, 20000, rc, 0);
|
|
|
|
if (!rc) {
|
|
sts = BC_STS_SUCCESS;
|
|
} else if (rc == -EBUSY) {
|
|
BCMLOG_ERR("Firmware command T/O\n");
|
|
sts = BC_STS_TIMEOUT;
|
|
} else if (rc == -EINTR) {
|
|
BCMLOG(BCMLOG_DBG, "FwCmd Wait Signal int.\n");
|
|
sts = BC_STS_IO_USER_ABORT;
|
|
} else {
|
|
BCMLOG_ERR("FwCmd IO Error.\n");
|
|
sts = BC_STS_IO_ERROR;
|
|
}
|
|
|
|
if (sts != BC_STS_SUCCESS) {
|
|
BCMLOG_ERR("FwCmd Failed.\n");
|
|
hw->pwr_lock--;
|
|
return sts;
|
|
}
|
|
|
|
/*Get the Responce Address*/
|
|
cmd_res_addr = bc_dec_reg_rd(hw->adp, Cpu2HstMbx1);
|
|
|
|
/*Read the Response*/
|
|
crystalhd_mem_rd(hw->adp, cmd_res_addr, FW_CMD_BUFF_SZ, res_buff);
|
|
|
|
hw->pwr_lock--;
|
|
|
|
if (res_buff[2] != C011_RET_SUCCESS) {
|
|
BCMLOG_ERR("res_buff[2] != C011_RET_SUCCESS\n");
|
|
return BC_STS_FW_CMD_ERR;
|
|
}
|
|
|
|
sts = crystalhd_fw_cmd_post_proc(hw, fw_cmd);
|
|
if (sts != BC_STS_SUCCESS)
|
|
BCMLOG_ERR("crystalhd_fw_cmd_post_proc Failed.\n");
|
|
|
|
return sts;
|
|
}
|
|
|
|
bool crystalhd_hw_interrupt(struct crystalhd_adp *adp, struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t intr_sts = 0;
|
|
uint32_t deco_intr = 0;
|
|
bool rc = 0;
|
|
|
|
if (!adp || !hw->dev_started)
|
|
return rc;
|
|
|
|
hw->stats.num_interrupts++;
|
|
hw->pwr_lock++;
|
|
|
|
deco_intr = bc_dec_reg_rd(adp, Stream2Host_Intr_Sts);
|
|
intr_sts = crystalhd_reg_rd(adp, INTR_INTR_STATUS);
|
|
|
|
if (intr_sts) {
|
|
/* let system know we processed interrupt..*/
|
|
rc = 1;
|
|
hw->stats.dev_interrupts++;
|
|
}
|
|
|
|
if (deco_intr && (deco_intr != 0xdeaddead)) {
|
|
|
|
if (deco_intr & 0x80000000) {
|
|
/*Set the Event and the status flag*/
|
|
if (hw->pfw_cmd_event) {
|
|
hw->fwcmd_evt_sts = 1;
|
|
crystalhd_set_event(hw->pfw_cmd_event);
|
|
}
|
|
}
|
|
|
|
if (deco_intr & BC_BIT(1))
|
|
crystalhd_hw_proc_pib(hw);
|
|
|
|
bc_dec_reg_wr(adp, Stream2Host_Intr_Sts, deco_intr);
|
|
/* FIXME: jarod: No udelay? might this be the real reason mini pci-e cards were stalling out? */
|
|
bc_dec_reg_wr(adp, Stream2Host_Intr_Sts, 0);
|
|
rc = 1;
|
|
}
|
|
|
|
/* Rx interrupts */
|
|
crystalhd_rx_isr(hw, intr_sts);
|
|
|
|
/* Tx interrupts*/
|
|
crystalhd_tx_isr(hw, intr_sts);
|
|
|
|
/* Clear interrupts */
|
|
if (rc) {
|
|
if (intr_sts)
|
|
crystalhd_reg_wr(adp, INTR_INTR_CLR_REG, intr_sts);
|
|
|
|
crystalhd_reg_wr(adp, INTR_EOI_CTRL, 1);
|
|
}
|
|
|
|
hw->pwr_lock--;
|
|
|
|
return rc;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_open(struct crystalhd_hw *hw, struct crystalhd_adp *adp)
|
|
{
|
|
if (!hw || !adp) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if (hw->dev_started)
|
|
return BC_STS_SUCCESS;
|
|
|
|
memset(hw, 0, sizeof(struct crystalhd_hw));
|
|
|
|
hw->adp = adp;
|
|
spin_lock_init(&hw->lock);
|
|
spin_lock_init(&hw->rx_lock);
|
|
/* FIXME: jarod: what are these magic numbers?!? */
|
|
hw->tx_ioq_tag_seed = 0x70023070;
|
|
hw->rx_pkt_tag_seed = 0x70029070;
|
|
|
|
hw->stop_pending = 0;
|
|
crystalhd_start_device(hw->adp);
|
|
hw->dev_started = true;
|
|
|
|
/* set initial core clock */
|
|
hw->core_clock_mhz = CLOCK_PRESET;
|
|
hw->prev_n = 0;
|
|
hw->pwr_lock = 0;
|
|
crystalhd_hw_set_core_clock(hw);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_close(struct crystalhd_hw *hw)
|
|
{
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
if (!hw->dev_started)
|
|
return BC_STS_SUCCESS;
|
|
|
|
/* Stop and DDR sleep will happen in here */
|
|
crystalhd_hw_suspend(hw);
|
|
hw->dev_started = false;
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_setup_dma_rings(struct crystalhd_hw *hw)
|
|
{
|
|
unsigned int i;
|
|
void *mem;
|
|
size_t mem_len;
|
|
dma_addr_t phy_addr;
|
|
enum BC_STATUS sts = BC_STS_SUCCESS;
|
|
struct crystalhd_rx_dma_pkt *rpkt;
|
|
|
|
if (!hw || !hw->adp) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
sts = crystalhd_hw_create_ioqs(hw);
|
|
if (sts != BC_STS_SUCCESS) {
|
|
BCMLOG_ERR("Failed to create IOQs..\n");
|
|
return sts;
|
|
}
|
|
|
|
mem_len = BC_LINK_MAX_SGLS * sizeof(struct dma_descriptor);
|
|
|
|
for (i = 0; i < BC_TX_LIST_CNT; i++) {
|
|
mem = bc_kern_dma_alloc(hw->adp, mem_len, &phy_addr);
|
|
if (mem) {
|
|
memset(mem, 0, mem_len);
|
|
} else {
|
|
BCMLOG_ERR("Insufficient Memory For TX\n");
|
|
crystalhd_hw_free_dma_rings(hw);
|
|
return BC_STS_INSUFF_RES;
|
|
}
|
|
/* rx_pkt_pool -- static memory allocation */
|
|
hw->tx_pkt_pool[i].desc_mem.pdma_desc_start = mem;
|
|
hw->tx_pkt_pool[i].desc_mem.phy_addr = phy_addr;
|
|
hw->tx_pkt_pool[i].desc_mem.sz = BC_LINK_MAX_SGLS *
|
|
sizeof(struct dma_descriptor);
|
|
hw->tx_pkt_pool[i].list_tag = 0;
|
|
|
|
/* Add TX dma requests to Free Queue..*/
|
|
sts = crystalhd_dioq_add(hw->tx_freeq,
|
|
&hw->tx_pkt_pool[i], false, 0);
|
|
if (sts != BC_STS_SUCCESS) {
|
|
crystalhd_hw_free_dma_rings(hw);
|
|
return sts;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < BC_RX_LIST_CNT; i++) {
|
|
rpkt = kzalloc(sizeof(*rpkt), GFP_KERNEL);
|
|
if (!rpkt) {
|
|
BCMLOG_ERR("Insufficient Memory For RX\n");
|
|
crystalhd_hw_free_dma_rings(hw);
|
|
return BC_STS_INSUFF_RES;
|
|
}
|
|
|
|
mem = bc_kern_dma_alloc(hw->adp, mem_len, &phy_addr);
|
|
if (mem) {
|
|
memset(mem, 0, mem_len);
|
|
} else {
|
|
BCMLOG_ERR("Insufficient Memory For RX\n");
|
|
crystalhd_hw_free_dma_rings(hw);
|
|
return BC_STS_INSUFF_RES;
|
|
}
|
|
rpkt->desc_mem.pdma_desc_start = mem;
|
|
rpkt->desc_mem.phy_addr = phy_addr;
|
|
rpkt->desc_mem.sz = BC_LINK_MAX_SGLS * sizeof(struct dma_descriptor);
|
|
rpkt->pkt_tag = hw->rx_pkt_tag_seed + i;
|
|
crystalhd_hw_free_rx_pkt(hw, rpkt);
|
|
}
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_free_dma_rings(struct crystalhd_hw *hw)
|
|
{
|
|
unsigned int i;
|
|
struct crystalhd_rx_dma_pkt *rpkt = NULL;
|
|
|
|
if (!hw || !hw->adp) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
/* Delete all IOQs.. */
|
|
crystalhd_hw_delete_ioqs(hw);
|
|
|
|
for (i = 0; i < BC_TX_LIST_CNT; i++) {
|
|
if (hw->tx_pkt_pool[i].desc_mem.pdma_desc_start) {
|
|
bc_kern_dma_free(hw->adp,
|
|
hw->tx_pkt_pool[i].desc_mem.sz,
|
|
hw->tx_pkt_pool[i].desc_mem.pdma_desc_start,
|
|
hw->tx_pkt_pool[i].desc_mem.phy_addr);
|
|
|
|
hw->tx_pkt_pool[i].desc_mem.pdma_desc_start = NULL;
|
|
}
|
|
}
|
|
|
|
BCMLOG(BCMLOG_DBG, "Releasing RX Pkt pool\n");
|
|
do {
|
|
rpkt = crystalhd_hw_alloc_rx_pkt(hw);
|
|
if (!rpkt)
|
|
break;
|
|
bc_kern_dma_free(hw->adp, rpkt->desc_mem.sz,
|
|
rpkt->desc_mem.pdma_desc_start,
|
|
rpkt->desc_mem.phy_addr);
|
|
kfree(rpkt);
|
|
} while (rpkt);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_post_tx(struct crystalhd_hw *hw, struct crystalhd_dio_req *ioreq,
|
|
hw_comp_callback call_back,
|
|
wait_queue_head_t *cb_event, uint32_t *list_id,
|
|
uint8_t data_flags)
|
|
{
|
|
struct tx_dma_pkt *tx_dma_packet = NULL;
|
|
uint32_t first_desc_u_addr, first_desc_l_addr;
|
|
uint32_t low_addr, high_addr;
|
|
union addr_64 desc_addr;
|
|
enum BC_STATUS sts, add_sts;
|
|
uint32_t dummy_index = 0;
|
|
unsigned long flags;
|
|
bool rc;
|
|
|
|
if (!hw || !ioreq || !call_back || !cb_event || !list_id) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
/*
|
|
* Since we hit code in busy condition very frequently,
|
|
* we will check the code in status first before
|
|
* checking the availability of free elem.
|
|
*
|
|
* This will avoid the Q fetch/add in normal condition.
|
|
*/
|
|
rc = crystalhd_code_in_full(hw->adp, ioreq->uinfo.xfr_len,
|
|
false, data_flags);
|
|
if (rc) {
|
|
hw->stats.cin_busy++;
|
|
return BC_STS_BUSY;
|
|
}
|
|
|
|
/* Get a list from TxFreeQ */
|
|
tx_dma_packet = (struct tx_dma_pkt *)crystalhd_dioq_fetch(hw->tx_freeq);
|
|
if (!tx_dma_packet) {
|
|
BCMLOG_ERR("No empty elements..\n");
|
|
return BC_STS_ERR_USAGE;
|
|
}
|
|
|
|
sts = crystalhd_xlat_sgl_to_dma_desc(ioreq,
|
|
&tx_dma_packet->desc_mem,
|
|
&dummy_index);
|
|
if (sts != BC_STS_SUCCESS) {
|
|
add_sts = crystalhd_dioq_add(hw->tx_freeq, tx_dma_packet,
|
|
false, 0);
|
|
if (add_sts != BC_STS_SUCCESS)
|
|
BCMLOG_ERR("double fault..\n");
|
|
|
|
return sts;
|
|
}
|
|
|
|
hw->pwr_lock++;
|
|
|
|
desc_addr.full_addr = tx_dma_packet->desc_mem.phy_addr;
|
|
low_addr = desc_addr.low_part;
|
|
high_addr = desc_addr.high_part;
|
|
|
|
tx_dma_packet->call_back = call_back;
|
|
tx_dma_packet->cb_event = cb_event;
|
|
tx_dma_packet->dio_req = ioreq;
|
|
|
|
spin_lock_irqsave(&hw->lock, flags);
|
|
|
|
if (hw->tx_list_post_index == 0) {
|
|
first_desc_u_addr = MISC1_TX_FIRST_DESC_U_ADDR_LIST0;
|
|
first_desc_l_addr = MISC1_TX_FIRST_DESC_L_ADDR_LIST0;
|
|
} else {
|
|
first_desc_u_addr = MISC1_TX_FIRST_DESC_U_ADDR_LIST1;
|
|
first_desc_l_addr = MISC1_TX_FIRST_DESC_L_ADDR_LIST1;
|
|
}
|
|
|
|
*list_id = tx_dma_packet->list_tag = hw->tx_ioq_tag_seed +
|
|
hw->tx_list_post_index;
|
|
|
|
hw->tx_list_post_index = (hw->tx_list_post_index + 1) % DMA_ENGINE_CNT;
|
|
|
|
spin_unlock_irqrestore(&hw->lock, flags);
|
|
|
|
|
|
/* Insert in Active Q..*/
|
|
crystalhd_dioq_add(hw->tx_actq, tx_dma_packet, false,
|
|
tx_dma_packet->list_tag);
|
|
|
|
/*
|
|
* Interrupt will come as soon as you write
|
|
* the valid bit. So be ready for that. All
|
|
* the initialization should happen before that.
|
|
*/
|
|
crystalhd_start_tx_dma_engine(hw);
|
|
crystalhd_reg_wr(hw->adp, first_desc_u_addr, desc_addr.high_part);
|
|
|
|
crystalhd_reg_wr(hw->adp, first_desc_l_addr, desc_addr.low_part | 0x01);
|
|
/* Be sure we set the valid bit ^^^^ */
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* This is a force cancel and we are racing with ISR.
|
|
*
|
|
* Will try to remove the req from ActQ before ISR gets it.
|
|
* If ISR gets it first then the completion happens in the
|
|
* normal path and we will return _STS_NO_DATA from here.
|
|
*
|
|
* FIX_ME: Not Tested the actual condition..
|
|
*/
|
|
enum BC_STATUS crystalhd_hw_cancel_tx(struct crystalhd_hw *hw, uint32_t list_id)
|
|
{
|
|
if (!hw || !list_id) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
crystalhd_stop_tx_dma_engine(hw);
|
|
crystalhd_hw_tx_req_complete(hw, list_id, BC_STS_IO_USER_ABORT);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_add_cap_buffer(struct crystalhd_hw *hw,
|
|
struct crystalhd_dio_req *ioreq, bool en_post)
|
|
{
|
|
struct crystalhd_rx_dma_pkt *rpkt;
|
|
uint32_t tag, uv_desc_ix = 0;
|
|
enum BC_STATUS sts;
|
|
|
|
if (!hw || !ioreq) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
rpkt = crystalhd_hw_alloc_rx_pkt(hw);
|
|
if (!rpkt) {
|
|
BCMLOG_ERR("Insufficient resources\n");
|
|
return BC_STS_INSUFF_RES;
|
|
}
|
|
|
|
rpkt->dio_req = ioreq;
|
|
tag = rpkt->pkt_tag;
|
|
|
|
sts = crystalhd_xlat_sgl_to_dma_desc(ioreq, &rpkt->desc_mem, &uv_desc_ix);
|
|
if (sts != BC_STS_SUCCESS)
|
|
return sts;
|
|
|
|
rpkt->uv_phy_addr = 0;
|
|
|
|
/* Store the address of UV in the rx packet for post*/
|
|
if (uv_desc_ix)
|
|
rpkt->uv_phy_addr = rpkt->desc_mem.phy_addr +
|
|
(sizeof(struct dma_descriptor) * (uv_desc_ix + 1));
|
|
|
|
if (en_post)
|
|
sts = crystalhd_hw_post_cap_buff(hw, rpkt);
|
|
else
|
|
sts = crystalhd_dioq_add(hw->rx_freeq, rpkt, false, tag);
|
|
|
|
return sts;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_get_cap_buffer(struct crystalhd_hw *hw,
|
|
struct BC_PIC_INFO_BLOCK *pib,
|
|
struct crystalhd_dio_req **ioreq)
|
|
{
|
|
struct crystalhd_rx_dma_pkt *rpkt;
|
|
uint32_t timeout = BC_PROC_OUTPUT_TIMEOUT / 1000;
|
|
uint32_t sig_pending = 0;
|
|
|
|
|
|
if (!hw || !ioreq || !pib) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
rpkt = crystalhd_dioq_fetch_wait(hw->rx_rdyq, timeout, &sig_pending);
|
|
if (!rpkt) {
|
|
if (sig_pending) {
|
|
BCMLOG(BCMLOG_INFO, "wait on frame time out %d\n", sig_pending);
|
|
return BC_STS_IO_USER_ABORT;
|
|
} else {
|
|
return BC_STS_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
rpkt->dio_req->uinfo.comp_flags = rpkt->flags;
|
|
|
|
if (rpkt->flags & COMP_FLAG_PIB_VALID)
|
|
memcpy(pib, &rpkt->pib, sizeof(*pib));
|
|
|
|
*ioreq = rpkt->dio_req;
|
|
|
|
crystalhd_hw_free_rx_pkt(hw, rpkt);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_start_capture(struct crystalhd_hw *hw)
|
|
{
|
|
struct crystalhd_rx_dma_pkt *rx_pkt;
|
|
enum BC_STATUS sts;
|
|
uint32_t i;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
/* This is start of capture.. Post to both the lists.. */
|
|
for (i = 0; i < DMA_ENGINE_CNT; i++) {
|
|
rx_pkt = crystalhd_dioq_fetch(hw->rx_freeq);
|
|
if (!rx_pkt)
|
|
return BC_STS_NO_DATA;
|
|
sts = crystalhd_hw_post_cap_buff(hw, rx_pkt);
|
|
if (BC_STS_SUCCESS != sts)
|
|
break;
|
|
|
|
}
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_stop_capture(struct crystalhd_hw *hw)
|
|
{
|
|
void *temp = NULL;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
crystalhd_stop_rx_dma_engine(hw);
|
|
|
|
do {
|
|
temp = crystalhd_dioq_fetch(hw->rx_freeq);
|
|
if (temp)
|
|
crystalhd_rx_pkt_rel_call_back(hw, temp);
|
|
} while (temp);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_pause(struct crystalhd_hw *hw)
|
|
{
|
|
hw->stats.pause_cnt++;
|
|
hw->stop_pending = 1;
|
|
|
|
if ((hw->rx_list_sts[0] == sts_free) &&
|
|
(hw->rx_list_sts[1] == sts_free))
|
|
crystalhd_hw_finalize_pause(hw);
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_unpause(struct crystalhd_hw *hw)
|
|
{
|
|
enum BC_STATUS sts;
|
|
uint32_t aspm;
|
|
|
|
hw->stop_pending = 0;
|
|
|
|
aspm = crystalhd_reg_rd(hw->adp, PCIE_DLL_DATA_LINK_CONTROL);
|
|
aspm &= ~ASPM_L1_ENABLE;
|
|
/* NAREN BCMLOG(BCMLOG_INFO, "aspm off\n"); */
|
|
crystalhd_reg_wr(hw->adp, PCIE_DLL_DATA_LINK_CONTROL, aspm);
|
|
|
|
sts = crystalhd_hw_start_capture(hw);
|
|
return sts;
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_suspend(struct crystalhd_hw *hw)
|
|
{
|
|
enum BC_STATUS sts;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
sts = crystalhd_put_ddr2sleep(hw);
|
|
if (sts != BC_STS_SUCCESS) {
|
|
BCMLOG_ERR("Failed to Put DDR To Sleep!!\n");
|
|
return BC_STS_ERROR;
|
|
}
|
|
|
|
if (!crystalhd_stop_device(hw->adp)) {
|
|
BCMLOG_ERR("Failed to Stop Device!!\n");
|
|
return BC_STS_ERROR;
|
|
}
|
|
|
|
return BC_STS_SUCCESS;
|
|
}
|
|
|
|
void crystalhd_hw_stats(struct crystalhd_hw *hw, struct crystalhd_hw_stats *stats)
|
|
{
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return;
|
|
}
|
|
|
|
/* if called w/NULL stats, its a req to zero out the stats */
|
|
if (!stats) {
|
|
memset(&hw->stats, 0, sizeof(hw->stats));
|
|
return;
|
|
}
|
|
|
|
hw->stats.freeq_count = crystalhd_dioq_count(hw->rx_freeq);
|
|
hw->stats.rdyq_count = crystalhd_dioq_count(hw->rx_rdyq);
|
|
memcpy(stats, &hw->stats, sizeof(*stats));
|
|
}
|
|
|
|
enum BC_STATUS crystalhd_hw_set_core_clock(struct crystalhd_hw *hw)
|
|
{
|
|
uint32_t reg, n, i;
|
|
uint32_t vco_mg, refresh_reg;
|
|
|
|
if (!hw) {
|
|
BCMLOG_ERR("Invalid Arguments\n");
|
|
return BC_STS_INV_ARG;
|
|
}
|
|
|
|
/* FIXME: jarod: wha? */
|
|
/*n = (hw->core_clock_mhz * 3) / 20 + 1; */
|
|
n = hw->core_clock_mhz/5;
|
|
|
|
if (n == hw->prev_n)
|
|
return BC_STS_CLK_NOCHG;
|
|
|
|
if (hw->pwr_lock > 0) {
|
|
/* BCMLOG(BCMLOG_INFO,"pwr_lock is %u\n", hw->pwr_lock) */
|
|
return BC_STS_CLK_NOCHG;
|
|
}
|
|
|
|
i = n * 27;
|
|
if (i < 560)
|
|
vco_mg = 0;
|
|
else if (i < 900)
|
|
vco_mg = 1;
|
|
else if (i < 1030)
|
|
vco_mg = 2;
|
|
else
|
|
vco_mg = 3;
|
|
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
|
|
|
|
reg &= 0xFFFFCFC0;
|
|
reg |= n;
|
|
reg |= vco_mg << 12;
|
|
|
|
BCMLOG(BCMLOG_INFO, "clock is moving to %d with n %d with vco_mg %d\n",
|
|
hw->core_clock_mhz, n, vco_mg);
|
|
|
|
/* Change the DRAM refresh rate to accomodate the new frequency */
|
|
/* refresh reg = ((refresh_rate * clock_rate)/16) - 1; rounding up*/
|
|
refresh_reg = (7 * hw->core_clock_mhz / 16);
|
|
bc_dec_reg_wr(hw->adp, SDRAM_REF_PARAM, ((1 << 12) | refresh_reg));
|
|
|
|
bc_dec_reg_wr(hw->adp, DecHt_PllACtl, reg);
|
|
|
|
i = 0;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
|
|
|
|
if (reg & 0x00020000) {
|
|
hw->prev_n = n;
|
|
/* FIXME: jarod: outputting a random "C" is... confusing... */
|
|
BCMLOG(BCMLOG_INFO, "C");
|
|
return BC_STS_SUCCESS;
|
|
} else {
|
|
msleep_interruptible(10);
|
|
}
|
|
}
|
|
BCMLOG(BCMLOG_INFO, "clk change failed\n");
|
|
return BC_STS_CLK_NOCHG;
|
|
}
|