Commit 7963eb43 authored by Jarod Wilson's avatar Jarod Wilson Committed by Greg Kroah-Hartman

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: default avatarJarod 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: default avatarScott Davilla <davilla@4pi.com>
Signed-off-by: default avatarJarod 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: default avatarJarod 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: default avatarManu Abraham <abraham.manu@gmail.com>
Signed-off-by: default avatarJarod 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: default avatarJarod 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: default avatarJarod Wilson <jarod@redhat.com>
Signed-off-by: default avatarNaren Sankar <nsankar@broadcom.com>
Signed-off-by: default avatarScott Davilla <davilla@4pi.com>
Signed-off-by: default avatarManu Abraham <abraham.manu@gmail.com>
Signed-off-by: default avatarJarod Wilson <jarod@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ca1f29c0
......@@ -139,5 +139,7 @@ source "drivers/staging/sm7xx/Kconfig"
source "drivers/staging/dt3155/Kconfig"
source "drivers/staging/crystalhd/Kconfig"
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
......@@ -51,3 +51,4 @@ obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan/
obj-$(CONFIG_PCMCIA_NETWAVE) += netwave/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
obj-$(CONFIG_DT3155) += dt3155/
obj-$(CONFIG_CRYSTALHD) += crystalhd/
config CRYSTALHD
tristate "Broadcom Crystal HD video decoder support"
depends on PCI
default n
help
Support for the Broadcom Crystal HD video decoder chipset
obj-$(CONFIG_CRYSTALHD) += crystalhd.o
crystalhd-objs := crystalhd_cmds.o \
crystalhd_hw.o \
crystalhd_lnx.o \
crystalhd_misc.o
- Testing
- Cleanup return codes
- Cleanup typedefs
- Cleanup all WIN* references
- Allocate an Accelerator device class specific Major number,
since we don't have any other open sourced accelerators, it is the only
one in that category for now.
A somewhat similar device is the DXR2/3
Please send patches to:
Greg Kroah-Hartman <greg@kroah.com>
Naren Sankar <nsankar@broadcom.com>
Jarod Wilson <jarod@wilsonet.com>
Scott Davilla <davilla@4pi.com>
Manu Abraham <abraham.manu@gmail.com>
/********************************************************************
* Copyright(c) 2006-2009 Broadcom Corporation.
*
* Name: bc_dts_defs.h
*
* Description: Common definitions for all components. Only types
* is allowed to be included from this file.
*
* AU
*
* HISTORY:
*
********************************************************************
* This header is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License.
*
* This header 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 Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this header. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************/
#ifndef _BC_DTS_DEFS_H_
#define _BC_DTS_DEFS_H_
#include "bc_dts_types.h"
/* BIT Mask */
#define BC_BIT(_x) (1 << (_x))
typedef enum _BC_STATUS {
BC_STS_SUCCESS = 0,
BC_STS_INV_ARG = 1,
BC_STS_BUSY = 2,
BC_STS_NOT_IMPL = 3,
BC_STS_PGM_QUIT = 4,
BC_STS_NO_ACCESS = 5,
BC_STS_INSUFF_RES = 6,
BC_STS_IO_ERROR = 7,
BC_STS_NO_DATA = 8,
BC_STS_VER_MISMATCH = 9,
BC_STS_TIMEOUT = 10,
BC_STS_FW_CMD_ERR = 11,
BC_STS_DEC_NOT_OPEN = 12,
BC_STS_ERR_USAGE = 13,
BC_STS_IO_USER_ABORT = 14,
BC_STS_IO_XFR_ERROR = 15,
BC_STS_DEC_NOT_STARTED = 16,
BC_STS_FWHEX_NOT_FOUND = 17,
BC_STS_FMT_CHANGE = 18,
BC_STS_HIF_ACCESS = 19,
BC_STS_CMD_CANCELLED = 20,
BC_STS_FW_AUTH_FAILED = 21,
BC_STS_BOOTLOADER_FAILED = 22,
BC_STS_CERT_VERIFY_ERROR = 23,
BC_STS_DEC_EXIST_OPEN = 24,
BC_STS_PENDING = 25,
BC_STS_CLK_NOCHG = 26,
/* Must be the last one.*/
BC_STS_ERROR = -1
} BC_STATUS;
/*------------------------------------------------------*
* Registry Key Definitions *
*------------------------------------------------------*/
#define BC_REG_KEY_MAIN_PATH "Software\\Broadcom\\MediaPC\\70010"
#define BC_REG_KEY_FWPATH "FirmwareFilePath"
#define BC_REG_KEY_SEC_OPT "DbgOptions"
/*
* Options:
*
* b[5] = Enable RSA KEY in EEPROM Support
* b[6] = Enable Old PIB scheme. (0 = Use PIB with video scheme)
*
* b[12] = Enable send message to NotifyIcon
*
*/
typedef enum _BC_SW_OPTIONS {
BC_OPT_DOSER_OUT_ENCRYPT = BC_BIT(3),
BC_OPT_LINK_OUT_ENCRYPT = BC_BIT(29),
} BC_SW_OPTIONS;
typedef struct _BC_REG_CONFIG{
uint32_t DbgOptions;
} BC_REG_CONFIG;
#if defined(__KERNEL__) || defined(__LINUX_USER__)
#else
/* Align data structures */
#define ALIGN(x) __declspec(align(x))
#endif
/* mode
* b[0]..b[7] = _DtsDeviceOpenMode
* b[8] = Load new FW
* b[9] = Load file play back FW
* b[10] = Disk format (0 for HD DVD and 1 for BLU ray)
* b[11]-b[15] = default output resolution
* b[16] = Skip TX CPB Buffer Check
* b[17] = Adaptive Output Encrypt/Scramble Scheme
* b[18]-b[31] = reserved for future use
*/
/* To allow multiple apps to open the device. */
enum _DtsDeviceOpenMode {
DTS_PLAYBACK_MODE = 0,
DTS_DIAG_MODE,
DTS_MONITOR_MODE,
DTS_HWINIT_MODE
};
/* To enable the filter to selectively enable/disable fixes or erratas */
enum _DtsDeviceFixMode {
DTS_LOAD_NEW_FW = BC_BIT(8),
DTS_LOAD_FILE_PLAY_FW = BC_BIT(9),
DTS_DISK_FMT_BD = BC_BIT(10),
/* b[11]-b[15] : Default output resolution */
DTS_SKIP_TX_CHK_CPB = BC_BIT(16),
DTS_ADAPTIVE_OUTPUT_PER = BC_BIT(17),
DTS_INTELLIMAP = BC_BIT(18),
/* b[19]-b[21] : select clock frequency */
DTS_PLAYBACK_DROP_RPT_MODE = BC_BIT(22)
};
#define DTS_DFLT_RESOLUTION(x) (x<<11)
#define DTS_DFLT_CLOCK(x) (x<<19)
/* F/W File Version corresponding to S/W Releases */
enum _FW_FILE_VER {
/* S/W release: 02.04.02 F/W release 2.12.2.0 */
BC_FW_VER_020402 = ((12<<16) | (2<<8) | (0))
};
/*------------------------------------------------------*
* Stream Types for DtsOpenDecoder() *
*------------------------------------------------------*/
enum _DtsOpenDecStreamTypes {
BC_STREAM_TYPE_ES = 0,
BC_STREAM_TYPE_PES = 1,
BC_STREAM_TYPE_TS = 2,
BC_STREAM_TYPE_ES_TSTAMP = 6,
};
/*------------------------------------------------------*
* Video Algorithms for DtsSetVideoParams() *
*------------------------------------------------------*/
enum _DtsSetVideoParamsAlgo {
BC_VID_ALGO_H264 = 0,
BC_VID_ALGO_MPEG2 = 1,
BC_VID_ALGO_VC1 = 4,
BC_VID_ALGO_VC1MP = 7,
};
/*------------------------------------------------------*
* MPEG Extension to the PPB *
*------------------------------------------------------*/
#define BC_MPEG_VALID_PANSCAN (1)
typedef struct _BC_PIB_EXT_MPEG {
uint32_t valid;
/* Always valid, defaults to picture size if no
* sequence display extension in the stream. */
uint32_t display_horizontal_size;
uint32_t display_vertical_size;
/* MPEG_VALID_PANSCAN
* Offsets are a copy values from the MPEG stream. */
uint32_t offset_count;
int32_t horizontal_offset[3];
int32_t vertical_offset[3];
} BC_PIB_EXT_MPEG;
/*------------------------------------------------------*
* H.264 Extension to the PPB *
*------------------------------------------------------*/
/* Bit definitions for 'other.h264.valid' field */
#define H264_VALID_PANSCAN (1)
#define H264_VALID_SPS_CROP (2)
#define H264_VALID_VUI (4)
typedef struct _BC_PIB_EXT_H264 {
/* 'valid' specifies which fields (or sets of
* fields) below are valid. If the corresponding
* bit in 'valid' is NOT set then that field(s)
* is (are) not initialized. */
uint32_t valid;
/* H264_VALID_PANSCAN */
uint32_t pan_scan_count;
int32_t pan_scan_left[3];
int32_t pan_scan_right[3];
int32_t pan_scan_top[3];
int32_t pan_scan_bottom[3];
/* H264_VALID_SPS_CROP */
int32_t sps_crop_left;
int32_t sps_crop_right;
int32_t sps_crop_top;
int32_t sps_crop_bottom;
/* H264_VALID_VUI */
uint32_t chroma_top;
uint32_t chroma_bottom;
} BC_PIB_EXT_H264;
/*------------------------------------------------------*
* VC1 Extension to the PPB *
*------------------------------------------------------*/
#define VC1_VALID_PANSCAN (1)
typedef struct _BC_PIB_EXT_VC1 {
uint32_t valid;
/* Always valid, defaults to picture size if no
* sequence display extension in the stream. */
uint32_t display_horizontal_size;
uint32_t display_vertical_size;
/* VC1 pan scan windows */
uint32_t num_panscan_windows;
int32_t ps_horiz_offset[4];
int32_t ps_vert_offset[4];
int32_t ps_width[4];
int32_t ps_height[4];
} BC_PIB_EXT_VC1;
/*------------------------------------------------------*
* Picture Information Block *
*------------------------------------------------------*/
#if defined(_WIN32) || defined(_WIN64) || defined(__LINUX_USER__)
/* Values for 'pulldown' field. '0' means no pulldown information
* was present for this picture. */
enum {
vdecNoPulldownInfo = 0,
vdecTop = 1,
vdecBottom = 2,
vdecTopBottom = 3,
vdecBottomTop = 4,
vdecTopBottomTop = 5,
vdecBottomTopBottom = 6,
vdecFrame_X2 = 7,
vdecFrame_X3 = 8,
vdecFrame_X1 = 9,
vdecFrame_X4 = 10,
};
/* Values for the 'frame_rate' field. */
enum {
vdecFrameRateUnknown = 0,
vdecFrameRate23_97,
vdecFrameRate24,
vdecFrameRate25,
vdecFrameRate29_97,
vdecFrameRate30,
vdecFrameRate50,
vdecFrameRate59_94,
vdecFrameRate60,
};
/* Values for the 'aspect_ratio' field. */
enum {
vdecAspectRatioUnknown = 0,
vdecAspectRatioSquare,
vdecAspectRatio12_11,
vdecAspectRatio10_11,
vdecAspectRatio16_11,
vdecAspectRatio40_33,
vdecAspectRatio24_11,
vdecAspectRatio20_11,
vdecAspectRatio32_11,
vdecAspectRatio80_33,
vdecAspectRatio18_11,
vdecAspectRatio15_11,
vdecAspectRatio64_33,
vdecAspectRatio160_99,
vdecAspectRatio4_3,
vdecAspectRatio16_9,
vdecAspectRatio221_1,
vdecAspectRatioOther = 255,
};
/* Values for the 'colour_primaries' field. */
enum {
vdecColourPrimariesUnknown = 0,
vdecColourPrimariesBT709,
vdecColourPrimariesUnspecified,
vdecColourPrimariesReserved,
vdecColourPrimariesBT470_2M = 4,
vdecColourPrimariesBT470_2BG,
vdecColourPrimariesSMPTE170M,
vdecColourPrimariesSMPTE240M,
vdecColourPrimariesGenericFilm,
};
enum {
vdecRESOLUTION_CUSTOM = 0x00000000, /* custom */
vdecRESOLUTION_480i = 0x00000001, /* 480i */
vdecRESOLUTION_1080i = 0x00000002, /* 1080i (1920x1080, 60i) */
vdecRESOLUTION_NTSC = 0x00000003, /* NTSC (720x483, 60i) */
vdecRESOLUTION_480p = 0x00000004, /* 480p (720x480, 60p) */
vdecRESOLUTION_720p = 0x00000005, /* 720p (1280x720, 60p) */
vdecRESOLUTION_PAL1 = 0x00000006, /* PAL_1 (720x576, 50i) */
vdecRESOLUTION_1080i25 = 0x00000007, /* 1080i25 (1920x1080, 50i) */
vdecRESOLUTION_720p50 = 0x00000008, /* 720p50 (1280x720, 50p) */
vdecRESOLUTION_576p = 0x00000009, /* 576p (720x576, 50p) */
vdecRESOLUTION_1080i29_97 = 0x0000000A, /* 1080i (1920x1080, 59.94i) */
vdecRESOLUTION_720p59_94 = 0x0000000B, /* 720p (1280x720, 59.94p) */
vdecRESOLUTION_SD_DVD = 0x0000000C, /* SD DVD (720x483, 60i) */
vdecRESOLUTION_480p656 = 0x0000000D, /* 480p (720x480, 60p), output bus width 8 bit, clock 74.25MHz */
vdecRESOLUTION_1080p23_976 = 0x0000000E, /* 1080p23_976 (1920x1080, 23.976p) */
vdecRESOLUTION_720p23_976 = 0x0000000F, /* 720p23_976 (1280x720p, 23.976p) */
vdecRESOLUTION_240p29_97 = 0x00000010, /* 240p (1440x240, 29.97p ) */
vdecRESOLUTION_240p30 = 0x00000011, /* 240p (1440x240, 30p) */
vdecRESOLUTION_288p25 = 0x00000012, /* 288p (1440x288p, 25p) */
vdecRESOLUTION_1080p29_97 = 0x00000013, /* 1080p29_97 (1920x1080, 29.97p) */
vdecRESOLUTION_1080p30 = 0x00000014, /* 1080p30 (1920x1080, 30p) */
vdecRESOLUTION_1080p24 = 0x00000015, /* 1080p24 (1920x1080, 24p) */
vdecRESOLUTION_1080p25 = 0x00000016, /* 1080p25 (1920x1080, 25p) */
vdecRESOLUTION_720p24 = 0x00000017, /* 720p24 (1280x720, 25p) */
vdecRESOLUTION_720p29_97 = 0x00000018, /* 720p29.97 (1280x720, 29.97p) */
vdecRESOLUTION_480p23_976 = 0x00000019, /* 480p23.976 (720*480, 23.976) */
vdecRESOLUTION_480p29_97 = 0x0000001A, /* 480p29.976 (720*480, 29.97p) */
vdecRESOLUTION_576p25 = 0x0000001B, /* 576p25 (720*576, 25p) */
/* For Zero Frame Rate */
vdecRESOLUTION_480p0 = 0x0000001C, /* 480p (720x480, 0p) */
vdecRESOLUTION_480i0 = 0x0000001D, /* 480i (720x480, 0i) */
vdecRESOLUTION_576p0 = 0x0000001E, /* 576p (720x576, 0p) */
vdecRESOLUTION_720p0 = 0x0000001F, /* 720p (1280x720, 0p) */
vdecRESOLUTION_1080p0 = 0x00000020, /* 1080p (1920x1080, 0p) */
vdecRESOLUTION_1080i0 = 0x00000021, /* 1080i (1920x1080, 0i) */
};
/* Bit definitions for 'flags' field */
#define VDEC_FLAG_EOS (0x0004)
#define VDEC_FLAG_FRAME (0x0000)
#define VDEC_FLAG_FIELDPAIR (0x0008)
#define VDEC_FLAG_TOPFIELD (0x0010)
#define VDEC_FLAG_BOTTOMFIELD (0x0018)
#define VDEC_FLAG_PROGRESSIVE_SRC (0x0000)
#define VDEC_FLAG_INTERLACED_SRC (0x0020)
#define VDEC_FLAG_UNKNOWN_SRC (0x0040)
#define VDEC_FLAG_BOTTOM_FIRST (0x0080)
#define VDEC_FLAG_LAST_PICTURE (0x0100)
#define VDEC_FLAG_PICTURE_META_DATA_PRESENT (0x40000)
#endif /* _WIN32 || _WIN64 */
enum _BC_OUTPUT_FORMAT {
MODE420 = 0x0,
MODE422_YUY2 = 0x1,
MODE422_UYVY = 0x2,
};
typedef struct _BC_PIC_INFO_BLOCK {
/* Common fields. */
uint64_t timeStamp; /* Timestamp */
uint32_t picture_number; /* Ordinal display number */
uint32_t width; /* pixels */
uint32_t height; /* pixels */
uint32_t chroma_format; /* 0x420, 0x422 or 0x444 */
uint32_t pulldown;
uint32_t flags;
uint32_t frame_rate;
uint32_t aspect_ratio;
uint32_t colour_primaries;
uint32_t picture_meta_payload;
uint32_t sess_num;
uint32_t ycom;
uint32_t custom_aspect_ratio_width_height;
uint32_t n_drop; /* number of non-reference frames remaining to be dropped */
/* Protocol-specific extensions. */
union {
BC_PIB_EXT_H264 h264;
BC_PIB_EXT_MPEG mpeg;
BC_PIB_EXT_VC1 vc1;
} other;
} BC_PIC_INFO_BLOCK, *PBC_PIC_INFO_BLOCK;
/*------------------------------------------------------*
* ProcOut Info *
*------------------------------------------------------*/
/* Optional flags for ProcOut Interface.*/
enum _POUT_OPTIONAL_IN_FLAGS_{
/* Flags from App to Device */
BC_POUT_FLAGS_YV12 = 0x01, /* Copy Data in YV12 format */
BC_POUT_FLAGS_STRIDE = 0x02, /* Stride size is valid. */
BC_POUT_FLAGS_SIZE = 0x04, /* Take size information from Application */
BC_POUT_FLAGS_INTERLACED = 0x08, /* copy only half the bytes */
BC_POUT_FLAGS_INTERLEAVED = 0x10, /* interleaved frame */
/* Flags from Device to APP */
BC_POUT_FLAGS_FMT_CHANGE = 0x10000, /* Data is not VALID when this flag is set */
BC_POUT_FLAGS_PIB_VALID = 0x20000, /* PIB Information valid */
BC_POUT_FLAGS_ENCRYPTED = 0x40000, /* Data is encrypted. */
BC_POUT_FLAGS_FLD_BOT = 0x80000, /* Bottom Field data */
};
#if defined(__KERNEL__) || defined(__LINUX_USER__)
typedef BC_STATUS(*dts_pout_callback)(void *shnd, uint32_t width, uint32_t height, uint32_t stride, void *pOut);
#else
typedef BC_STATUS(*dts_pout_callback)(void *shnd, uint32_t width, uint32_t height, uint32_t stride, struct _BC_DTS_PROC_OUT *pOut);
#endif
/* Line 21 Closed Caption */
/* User Data */
#define MAX_UD_SIZE 1792 /* 1920 - 128 */
typedef struct _BC_DTS_PROC_OUT {
uint8_t *Ybuff; /* Caller Supplied buffer for Y data */
uint32_t YbuffSz; /* Caller Supplied Y buffer size */
uint32_t YBuffDoneSz; /* Transferred Y datasize */
uint8_t *UVbuff; /* Caller Supplied buffer for UV data */
uint32_t UVbuffSz; /* Caller Supplied UV buffer size */
uint32_t UVBuffDoneSz; /* Transferred UV data size */
uint32_t StrideSz; /* Caller supplied Stride Size */
uint32_t PoutFlags; /* Call IN Flags */
uint32_t discCnt; /* Picture discontinuity count */
BC_PIC_INFO_BLOCK PicInfo; /* Picture Information Block Data */
/* Line 21 Closed Caption */
/* User Data */
uint32_t UserDataSz;
uint8_t UserData[MAX_UD_SIZE];
void *hnd;
dts_pout_callback AppCallBack;
uint8_t DropFrames;
uint8_t b422Mode; /* Picture output Mode */
uint8_t bPibEnc; /* PIB encrypted */
uint8_t bRevertScramble;
} BC_DTS_PROC_OUT;
typedef struct _BC_DTS_STATUS {
uint8_t ReadyListCount; /* Number of frames in ready list (reported by driver) */
uint8_t FreeListCount; /* Number of frame buffers free. (reported by driver) */
uint8_t PowerStateChange; /* Number of active state power transitions (reported by driver) */
uint8_t reserved_[1];
uint32_t FramesDropped; /* Number of frames dropped. (reported by DIL) */
uint32_t FramesCaptured; /* Number of frames captured. (reported by DIL) */
uint32_t FramesRepeated; /* Number of frames repeated. (reported by DIL) */
uint32_t InputCount; /* Times compressed video has been sent to the HW.
* i.e. Successful DtsProcInput() calls (reported by DIL) */
uint64_t InputTotalSize; /* Amount of compressed video that has been sent to the HW.
* (reported by DIL) */
uint32_t InputBusyCount; /* Times compressed video has attempted to be sent to the HW
* but the input FIFO was full. (reported by DIL) */
uint32_t PIBMissCount; /* Amount of times a PIB is invalid. (reported by DIL) */
uint32_t cpbEmptySize; /* supported only for H.264, specifically changed for
* Adobe. Report size of CPB buffer available.
* Reported by DIL */
uint64_t NextTimeStamp; /* TimeStamp of the next picture that will be returned
* by a call to ProcOutput. Added for Adobe. Reported
* back from the driver */
uint8_t reserved__[16];
} BC_DTS_STATUS;
#define BC_SWAP32(_v) \
((((_v) & 0xFF000000)>>24)| \
(((_v) & 0x00FF0000)>>8)| \
(((_v) & 0x0000FF00)<<8)| \
(((_v) & 0x000000FF)<<24))
#define WM_AGENT_TRAYICON_DECODER_OPEN 10001
#define WM_AGENT_TRAYICON_DECODER_CLOSE 10002
#define WM_AGENT_TRAYICON_DECODER_START 10003
#define WM_AGENT_TRAYICON_DECODER_STOP 10004
#define WM_AGENT_TRAYICON_DECODER_RUN 10005
#define WM_AGENT_TRAYICON_DECODER_PAUSE 10006
#endif /* _BC_DTS_DEFS_H_ */
/********************************************************************
* Copyright(c) 2006-2009 Broadcom Corporation.
*
* Name: bc_dts_glob_lnx.h
*
* Description: Wrapper to Windows dts_glob.h for Link-Linux usage.
* The idea is to define additional Linux related defs
* in this file to avoid changes to existing Windows
* glob file.
*
* AU
*
* HISTORY:
*
********************************************************************
* This header is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License.
*
* This header 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 Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this header. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************/
#ifndef _BC_DTS_GLOB_LNX_H_
#define _BC_DTS_GLOB_LNX_H_
#ifdef __LINUX_USER__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#include <time.h>
#include <arpa/inet.h>
#include <asm/param.h>
#include <linux/ioctl.h>
#include <sys/select.h>
#define DRVIFLIB_INT_API
#endif
#include "bc_dts_defs.h"
#include "bcm_70012_regs.h" /* Link Register defs */
#define CRYSTALHD_API_NAME "crystalhd"
#define CRYSTALHD_API_DEV_NAME "/dev/crystalhd"
/*
* These are SW stack tunable parameters shared
* between the driver and the application.
*/
enum _BC_DTS_GLOBALS {
BC_MAX_FW_CMD_BUFF_SZ = 0x40, /* FW passthrough cmd/rsp buffer size */
PCI_CFG_SIZE = 256, /* PCI config size buffer */
BC_IOCTL_DATA_POOL_SIZE = 8, /* BC_IOCTL_DATA Pool size */
BC_LINK_MAX_OPENS = 3, /* Maximum simultaneous opens*/
BC_LINK_MAX_SGLS = 1024, /* Maximum SG elements 4M/4K */
BC_TX_LIST_CNT = 2, /* Max Tx DMA Rings */
BC_RX_LIST_CNT = 8, /* Max Rx DMA Rings*/
BC_PROC_OUTPUT_TIMEOUT = 3000, /* Milliseconds */
BC_INFIFO_THRESHOLD = 0x10000,
};
typedef struct _BC_CMD_REG_ACC {
uint32_t Offset;
uint32_t Value;
} BC_CMD_REG_ACC;
typedef struct _BC_CMD_DEV_MEM {
uint32_t StartOff;
uint32_t NumDwords;
uint32_t Rsrd;
} BC_CMD_DEV_MEM;
/* FW Passthrough command structure */
enum _bc_fw_cmd_flags {
BC_FW_CMD_FLAGS_NONE = 0,
BC_FW_CMD_PIB_QS = 0x01,
};
typedef struct _BC_FW_CMD {
uint32_t cmd[BC_MAX_FW_CMD_BUFF_SZ];
uint32_t rsp[BC_MAX_FW_CMD_BUFF_SZ];
uint32_t flags;
uint32_t add_data;
} BC_FW_CMD, *PBC_FW_CMD;
typedef struct _BC_HW_TYPE {
uint16_t PciDevId;
uint16_t PciVenId;
uint8_t HwRev;
uint8_t Align[3];
} BC_HW_TYPE;
typedef struct _BC_PCI_CFG {
uint32_t Size;
uint32_t Offset;
uint8_t pci_cfg_space[PCI_CFG_SIZE];
} BC_PCI_CFG;
typedef struct _BC_VERSION_INFO_ {
uint8_t DriverMajor;
uint8_t DriverMinor;
uint16_t DriverRevision;
} BC_VERSION_INFO;
typedef struct _BC_START_RX_CAP_ {
uint32_t Rsrd;
uint32_t StartDeliveryThsh;
uint32_t PauseThsh;
uint32_t ResumeThsh;
} BC_START_RX_CAP;
typedef struct _BC_FLUSH_RX_CAP_ {
uint32_t Rsrd;
uint32_t bDiscardOnly;
} BC_FLUSH_RX_CAP;
typedef struct _BC_DTS_STATS {
uint8_t drvRLL;
uint8_t drvFLL;
uint8_t eosDetected;
uint8_t pwr_state_change;
/* Stats from App */
uint32_t opFrameDropped;
uint32_t opFrameCaptured;
uint32_t ipSampleCnt;
uint64_t ipTotalSize;
uint32_t reptdFrames;
uint32_t pauseCount;
uint32_t pibMisses;
uint32_t discCounter;
/* Stats from Driver */
uint32_t TxFifoBsyCnt;
uint32_t intCount;
uint32_t DrvIgnIntrCnt;
uint32_t DrvTotalFrmDropped;
uint32_t DrvTotalHWErrs;
uint32_t DrvTotalPIBFlushCnt;
uint32_t DrvTotalFrmCaptured;
uint32_t DrvPIBMisses;
uint32_t DrvPauseTime;
uint32_t DrvRepeatedFrms;
uint32_t res1[13];
} BC_DTS_STATS;
typedef struct _BC_PROC_INPUT_ {
uint8_t *pDmaBuff;
uint32_t BuffSz;
uint8_t Mapped;
uint8_t Encrypted;
uint8_t Rsrd[2];
uint32_t DramOffset; /* For debug use only */
} BC_PROC_INPUT, *PBC_PROC_INPUT;
typedef struct _BC_DEC_YUV_BUFFS {
uint32_t b422Mode;
uint8_t *YuvBuff;
uint32_t YuvBuffSz;
uint32_t UVbuffOffset;
uint32_t YBuffDoneSz;
uint32_t UVBuffDoneSz;
uint32_t RefCnt;
} BC_DEC_YUV_BUFFS;
enum _DECOUT_COMPLETION_FLAGS{
COMP_FLAG_NO_INFO = 0x00,
COMP_FLAG_FMT_CHANGE = 0x01,
COMP_FLAG_PIB_VALID = 0x02,
COMP_FLAG_DATA_VALID = 0x04,
COMP_FLAG_DATA_ENC = 0x08,
COMP_FLAG_DATA_BOT = 0x10,
};
typedef struct _BC_DEC_OUT_BUFF{
BC_DEC_YUV_BUFFS OutPutBuffs;
BC_PIC_INFO_BLOCK PibInfo;
uint32_t Flags;
uint32_t BadFrCnt;
} BC_DEC_OUT_BUFF;
typedef struct _BC_NOTIFY_MODE {
uint32_t Mode;
uint32_t Rsvr[3];
} BC_NOTIFY_MODE;
typedef struct _BC_CLOCK {
uint32_t clk;
uint32_t Rsvr[3];
} BC_CLOCK;
typedef struct _BC_IOCTL_DATA {
BC_STATUS RetSts;
uint32_t IoctlDataSz;
uint32_t Timeout;
union {
BC_CMD_REG_ACC regAcc;
BC_CMD_DEV_MEM devMem;
BC_FW_CMD fwCmd;
BC_HW_TYPE hwType;
BC_PCI_CFG pciCfg;
BC_VERSION_INFO VerInfo;
BC_PROC_INPUT ProcInput;
BC_DEC_YUV_BUFFS RxBuffs;
BC_DEC_OUT_BUFF DecOutData;
BC_START_RX_CAP RxCap;
BC_FLUSH_RX_CAP FlushRxCap;
BC_DTS_STATS drvStat;
BC_NOTIFY_MODE NotifyMode;
BC_CLOCK clockValue;
} u;
struct _BC_IOCTL_DATA *next;
} BC_IOCTL_DATA;
typedef enum _BC_DRV_CMD{
DRV_CMD_VERSION = 0, /* Get SW version */
DRV_CMD_GET_HWTYPE, /* Get HW version and type Dozer/Tank */
DRV_CMD_REG_RD, /* Read Device Register */
DRV_CMD_REG_WR, /* Write Device Register */
DRV_CMD_FPGA_RD, /* Read FPGA Register */
DRV_CMD_FPGA_WR, /* Wrtie FPGA Reister */
DRV_CMD_MEM_RD, /* Read Device Memory */
DRV_CMD_MEM_WR, /* Write Device Memory */
DRV_CMD_RD_PCI_CFG, /* Read PCI Config Space */
DRV_CMD_WR_PCI_CFG, /* Write the PCI Configuration Space*/
DRV_CMD_FW_DOWNLOAD, /* Download Firmware */
DRV_ISSUE_FW_CMD, /* Issue FW Cmd (pass through mode) */
DRV_CMD_PROC_INPUT, /* Process Input Sample */
DRV_CMD_ADD_RXBUFFS, /* Add Rx side buffers to driver pool */
DRV_CMD_FETCH_RXBUFF, /* Get Rx DMAed buffer */
DRV_CMD_START_RX_CAP, /* Start Rx Buffer Capture */
DRV_CMD_FLUSH_RX_CAP, /* Stop the capture for now...we will enhance this later*/
DRV_CMD_GET_DRV_STAT, /* Get Driver Internal Statistics */
DRV_CMD_RST_DRV_STAT, /* Reset Driver Internal Statistics */
DRV_CMD_NOTIFY_MODE, /* Notify the Mode to driver in which the application is Operating*/
DRV_CMD_CHANGE_CLOCK, /* Change the core clock to either save power or improve performance */
/* MUST be the last one.. */
DRV_CMD_END, /* End of the List.. */
} BC_DRV_CMD;
#define BC_IOC_BASE 'b'
#define BC_IOC_VOID _IOC_NONE
#define BC_IOC_IOWR(nr, type) _IOWR(BC_IOC_BASE, nr, type)
#define BC_IOCTL_MB BC_IOCTL_DATA
#define BCM_IOC_GET_VERSION BC_IOC_IOWR(DRV_CMD_VERSION, BC_IOCTL_MB)
#define BCM_IOC_GET_HWTYPE BC_IOC_IOWR(DRV_CMD_GET_HWTYPE, BC_IOCTL_MB)
#define BCM_IOC_REG_RD BC_IOC_IOWR(DRV_CMD_REG_RD, BC_IOCTL_MB)
#define BCM_IOC_REG_WR BC_IOC_IOWR(DRV_CMD_REG_WR, BC_IOCTL_MB)
#define BCM_IOC_MEM_RD BC_IOC_IOWR(DRV_CMD_MEM_RD, BC_IOCTL_MB)
#define BCM_IOC_MEM_WR BC_IOC_IOWR(DRV_CMD_MEM_WR, BC_IOCTL_MB)
#define BCM_IOC_FPGA_RD BC_IOC_IOWR(DRV_CMD_FPGA_RD, BC_IOCTL_MB)
#define BCM_IOC_FPGA_WR BC_IOC_IOWR(DRV_CMD_FPGA_WR, BC_IOCTL_MB)
#define BCM_IOC_RD_PCI_CFG BC_IOC_IOWR(DRV_CMD_RD_PCI_CFG, BC_IOCTL_MB)
#define BCM_IOC_WR_PCI_CFG BC_IOC_IOWR(DRV_CMD_WR_PCI_CFG, BC_IOCTL_MB)
#define BCM_IOC_PROC_INPUT BC_IOC_IOWR(DRV_CMD_PROC_INPUT, BC_IOCTL_MB)
#define BCM_IOC_ADD_RXBUFFS BC_IOC_IOWR(DRV_CMD_ADD_RXBUFFS, BC_IOCTL_MB)
#define BCM_IOC_FETCH_RXBUFF BC_IOC_IOWR(DRV_CMD_FETCH_RXBUFF, BC_IOCTL_MB)
#define BCM_IOC_FW_CMD BC_IOC_IOWR(DRV_ISSUE_FW_CMD, BC_IOCTL_MB)
#define BCM_IOC_START_RX_CAP BC_IOC_IOWR(DRV_CMD_START_RX_CAP, BC_IOCTL_MB)
#define BCM_IOC_FLUSH_RX_CAP BC_IOC_IOWR(DRV_CMD_FLUSH_RX_CAP, BC_IOCTL_MB)
#define BCM_IOC_GET_DRV_STAT BC_IOC_IOWR(DRV_CMD_GET_DRV_STAT, BC_IOCTL_MB)
#define BCM_IOC_RST_DRV_STAT BC_IOC_IOWR(DRV_CMD_RST_DRV_STAT, BC_IOCTL_MB)
#define BCM_IOC_NOTIFY_MODE BC_IOC_IOWR(DRV_CMD_NOTIFY_MODE, BC_IOCTL_MB)
#define BCM_IOC_FW_DOWNLOAD BC_IOC_IOWR(DRV_CMD_FW_DOWNLOAD, BC_IOCTL_MB)
#define BCM_IOC_CHG_CLK BC_IOC_IOWR(DRV_CMD_CHANGE_CLOCK, BC_IOCTL_MB)
#define BCM_IOC_END BC_IOC_VOID
/* Wrapper for main IOCTL data */
typedef struct _crystalhd_ioctl_data {
BC_IOCTL_DATA udata; /* IOCTL from App..*/
uint32_t u_id; /* Driver specific user ID */
uint32_t cmd; /* Cmd ID for driver's use. */
void *add_cdata; /* Additional command specific data..*/
uint32_t add_cdata_sz; /* Additional command specific data size */
struct _crystalhd_ioctl_data *next; /* List/Fifo management */
} crystalhd_ioctl_data;
enum _crystalhd_kmod_ver{
crystalhd_kmod_major = 0,
crystalhd_kmod_minor = 9,
crystalhd_kmod_rev = 27,
};
#endif
/********************************************************************
* Copyright(c) 2006-2009 Broadcom Corporation.
*
* Name: bc_dts_types.h
*
* Description: Data types
*
* AU
*
* HISTORY:
*
********************************************************************
* This header is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License.
*
* This header 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 Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this header. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************/
#ifndef _BC_DTS_TYPES_H_
#define _BC_DTS_TYPES_H_
#if defined(_WIN64) || defined(_WIN32)
typedef uint32_t U32;
typedef int32_t S32;
typedef uint16_t U16;
typedef int16_t S16;
typedef unsigned char U8;
typedef char S8;
#endif
#ifndef PVOID
typedef void *PVOID;
#endif
#ifndef BOOL
typedef int BOOL;
#endif
#ifdef WIN32
typedef unsigned __int64 U64;
#elif defined(_WIN64)
typedef uint64_t U64;
#endif
#ifdef _WIN64
#if !(defined(POINTER_32))
#define POINTER_32 __ptr32
#endif
#else /* _WIN32 */
#define POINTER_32
#endif
#if defined(__KERNEL__) || defined(__LINUX_USER__)
#ifdef __LINUX_USER__ /* Don't include these for KERNEL */
typedef uint32_t ULONG;
typedef int32_t LONG;
typedef void *HANDLE;
typedef void VOID;
typedef void *LPVOID;
typedef uint32_t DWORD;
typedef uint32_t UINT32;
typedef uint32_t *LPDWORD;
typedef unsigned char *PUCHAR;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define TEXT
#else
/* For Kernel usage.. */
typedef bool bc_bool_t;
#endif
#else
#ifndef uint64_t
typedef struct _uint64_t {
uint32_t low_dw;
uint32_t hi_dw;
} uint64_t;
#endif
#ifndef int32_t
typedef signed long int32_t;
#endif
#ifndef uint32_t
typedef unsigned long uint32_t;
#endif
#ifndef uint16_t
typedef unsigned short uint16_t;
#endif
#ifndef uint8_t
typedef unsigned char uint8_t;
#endif
#endif
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_cmds . c
*
* Description:
* BCM70010 Linux driver user command interfaces.
*
* 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_cmds.h"
#include "crystalhd_hw.h"
static struct crystalhd_user *bc_cproc_get_uid(struct crystalhd_cmd *ctx)
{
struct crystalhd_user *user = NULL;
int i;
for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
if (!ctx->user[i].in_use) {
user = &ctx->user[i];
break;
}
}
return user;
}
static int bc_cproc_get_user_count(struct crystalhd_cmd *ctx)
{
int i, count = 0;
for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
if (ctx->user[i].in_use)
count++;
}
return count;
}
static void bc_cproc_mark_pwr_state(struct crystalhd_cmd *ctx)
{
int i;
for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
if (!ctx->user[i].in_use)
continue;
if (ctx->user[i].mode == DTS_DIAG_MODE ||
ctx->user[i].mode == DTS_PLAYBACK_MODE) {
ctx->pwr_state_change = 1;
break;
}
}
}
static BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
int rc = 0, i = 0;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
if (ctx->user[idata->u_id].mode != DTS_MODE_INV) {
BCMLOG_ERR("Close the handle first..\n");
return BC_STS_ERR_USAGE;
}
if (idata->udata.u.NotifyMode.Mode == DTS_MONITOR_MODE) {
ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
return BC_STS_SUCCESS;
}
if (ctx->state != BC_LINK_INVALID) {
BCMLOG_ERR("Link invalid state %d \n", ctx->state);
return BC_STS_ERR_USAGE;
}
/* Check for duplicate playback sessions..*/
for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
if (ctx->user[i].mode == DTS_DIAG_MODE ||
ctx->user[i].mode == DTS_PLAYBACK_MODE) {
BCMLOG_ERR("multiple playback sessions are not "
"supported..\n");
return BC_STS_ERR_USAGE;
}
}
ctx->cin_wait_exit = 0;
ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
/* Setup mmap pool for uaddr sgl mapping..*/
rc = crystalhd_create_dio_pool(ctx->adp, BC_LINK_MAX_SGLS);
if (rc)
return BC_STS_ERROR;
/* Setup Hardware DMA rings */
return crystalhd_hw_setup_dma_rings(&ctx->hw_ctx);
}
static BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
idata->udata.u.VerInfo.DriverMajor = crystalhd_kmod_major;
idata->udata.u.VerInfo.DriverMinor = crystalhd_kmod_minor;
idata->udata.u.VerInfo.DriverRevision = crystalhd_kmod_rev;
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
{
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
crystalhd_pci_cfg_rd(ctx->adp, 0, 2,
(uint32_t *)&idata->udata.u.hwType.PciVenId);
crystalhd_pci_cfg_rd(ctx->adp, 2, 2,
(uint32_t *)&idata->udata.u.hwType.PciDevId);
crystalhd_pci_cfg_rd(ctx->adp, 8, 1,
(uint32_t *)&idata->udata.u.hwType.HwRev);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
if (!ctx || !idata)
return BC_STS_INV_ARG;
idata->udata.u.regAcc.Value = bc_dec_reg_rd(ctx->adp,
idata->udata.u.regAcc.Offset);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
if (!ctx || !idata)
return BC_STS_INV_ARG;
bc_dec_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
idata->udata.u.regAcc.Value);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
if (!ctx || !idata)
return BC_STS_INV_ARG;
idata->udata.u.regAcc.Value = crystalhd_reg_rd(ctx->adp,
idata->udata.u.regAcc.Offset);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
if (!ctx || !idata)
return BC_STS_INV_ARG;
crystalhd_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
idata->udata.u.regAcc.Value);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata || !idata->add_cdata)
return BC_STS_INV_ARG;
if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
BCMLOG_ERR("insufficient buffer\n");
return BC_STS_INV_ARG;
}
sts = crystalhd_mem_rd(ctx->adp, idata->udata.u.devMem.StartOff,
idata->udata.u.devMem.NumDwords,
(uint32_t *)idata->add_cdata);
return sts;
}
static BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata || !idata->add_cdata)
return BC_STS_INV_ARG;
if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
BCMLOG_ERR("insufficient buffer\n");
return BC_STS_INV_ARG;
}
sts = crystalhd_mem_wr(ctx->adp, idata->udata.u.devMem.StartOff,
idata->udata.u.devMem.NumDwords,
(uint32_t *)idata->add_cdata);
return sts;
}
static BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
uint32_t ix, cnt, off, len;
BC_STATUS sts = BC_STS_SUCCESS;
uint32_t *temp;
if (!ctx || !idata)
return BC_STS_INV_ARG;
temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
off = idata->udata.u.pciCfg.Offset;
len = idata->udata.u.pciCfg.Size;
if (len <= 4)
return crystalhd_pci_cfg_rd(ctx->adp, off, len, temp);
/* Truncate to dword alignment..*/
len = 4;
cnt = idata->udata.u.pciCfg.Size / len;
for (ix = 0; ix < cnt; ix++) {
sts = crystalhd_pci_cfg_rd(ctx->adp, off, len, &temp[ix]);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("config read : %d\n", sts);
return sts;
}
off += len;
}
return sts;
}
static BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
uint32_t ix, cnt, off, len;
BC_STATUS sts = BC_STS_SUCCESS;
uint32_t *temp;
if (!ctx || !idata)
return BC_STS_INV_ARG;
temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
off = idata->udata.u.pciCfg.Offset;
len = idata->udata.u.pciCfg.Size;
if (len <= 4)
return crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[0]);
/* Truncate to dword alignment..*/
len = 4;
cnt = idata->udata.u.pciCfg.Size / len;
for (ix = 0; ix < cnt; ix++) {
sts = crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[ix]);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("config write : %d\n", sts);
return sts;
}
off += len;
}
return sts;
}
static BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata || !idata->add_cdata || !idata->add_cdata_sz) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
if (ctx->state != BC_LINK_INVALID) {
BCMLOG_ERR("Link invalid state %d \n", ctx->state);
return BC_STS_ERR_USAGE;
}
sts = crystalhd_download_fw(ctx->adp, (uint8_t *)idata->add_cdata,
idata->add_cdata_sz);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("Firmware Download Failure!! - %d\n", sts);
} else
ctx->state |= BC_LINK_INIT;
return sts;
}
/*
* We use the FW_CMD interface to sync up playback state with application
* and firmware. This function will perform the required pre and post
* processing of the Firmware commands.
*
* Pause -
* Disable capture after decoder pause.
* Resume -
* First enable capture and issue decoder resume command.
* Flush -
* Abort pending input transfers and issue decoder flush command.
*
*/
static BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
{
BC_STATUS sts;
uint32_t *cmd;
if (!(ctx->state & BC_LINK_INIT)) {
BCMLOG_ERR("Link invalid state %d \n", ctx->state);
return BC_STS_ERR_USAGE;
}
cmd = idata->udata.u.fwCmd.cmd;
/* Pre-Process */
if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
if (!cmd[3]) {
ctx->state &= ~BC_LINK_PAUSED;
crystalhd_hw_unpause(&ctx->hw_ctx);
}
} else if (cmd[0] == eCMD_C011_DEC_CHAN_FLUSH) {
BCMLOG(BCMLOG_INFO, "Flush issued\n");
if (cmd[3])
ctx->cin_wait_exit = 1;
}
sts = crystalhd_do_fw_cmd(&ctx->hw_ctx, &idata->udata.u.fwCmd);
if (sts != BC_STS_SUCCESS) {
BCMLOG(BCMLOG_INFO, "fw cmd %x failed\n", cmd[0]);
return sts;
}
/* Post-Process */
if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
if (cmd[3]) {
ctx->state |= BC_LINK_PAUSED;
crystalhd_hw_pause(&ctx->hw_ctx);
}
}
return sts;
}
static void bc_proc_in_completion(crystalhd_dio_req *dio_hnd,
wait_queue_head_t *event, BC_STATUS sts)
{
if (!dio_hnd || !event) {
BCMLOG_ERR("Invalid Arg!!\n");
return;
}
if (sts == BC_STS_IO_USER_ABORT)
return;
dio_hnd->uinfo.comp_sts = sts;
dio_hnd->uinfo.ev_sts = 1;
crystalhd_set_event(event);
}
static BC_STATUS bc_cproc_codein_sleep(struct crystalhd_cmd *ctx)
{
wait_queue_head_t sleep_ev;
int rc = 0;
if (ctx->state & BC_LINK_SUSPEND)
return BC_STS_IO_USER_ABORT;
if (ctx->cin_wait_exit) {
ctx->cin_wait_exit = 0;
return BC_STS_CMD_CANCELLED;
}
crystalhd_create_event(&sleep_ev);
crystalhd_wait_on_event(&sleep_ev, 0, 100, rc, 0);
if (rc == -EINTR)
return BC_STS_IO_USER_ABORT;
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata,
crystalhd_dio_req *dio)
{
uint32_t tx_listid = 0;
BC_STATUS sts = BC_STS_SUCCESS;
wait_queue_head_t event;
int rc = 0;
if (!ctx || !idata || !dio) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
crystalhd_create_event(&event);
ctx->tx_list_id = 0;
/* msleep_interruptible(2000); */
sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, bc_proc_in_completion,
&event, &tx_listid,
idata->udata.u.ProcInput.Encrypted);
while (sts == BC_STS_BUSY) {
sts = bc_cproc_codein_sleep(ctx);
if (sts != BC_STS_SUCCESS)
break;
sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio,
bc_proc_in_completion,
&event, &tx_listid,
idata->udata.u.ProcInput.Encrypted);
}
if (sts != BC_STS_SUCCESS) {
BCMLOG(BCMLOG_DBG, "_hw_txdma returning sts:%d\n", sts);
return sts;
}
if (ctx->cin_wait_exit)
ctx->cin_wait_exit = 0;
ctx->tx_list_id = tx_listid;
/* _post() succeeded.. wait for the completion. */
crystalhd_wait_on_event(&event, (dio->uinfo.ev_sts), 3000, rc, 0);
ctx->tx_list_id = 0;
if (!rc) {
return dio->uinfo.comp_sts;
} else if (rc == -EBUSY) {
BCMLOG(BCMLOG_DBG, "_tx_post() T/O \n");
sts = BC_STS_TIMEOUT;
} else if (rc == -EINTR) {
BCMLOG(BCMLOG_DBG, "Tx Wait Signal int.\n");
sts = BC_STS_IO_USER_ABORT;
} else {
sts = BC_STS_IO_ERROR;
}
/* We are cancelling the IO from the same context as the _post().
* so no need to wait on the event again.. the return itself
* ensures the release of our resources.
*/
crystalhd_hw_cancel_tx(&ctx->hw_ctx, tx_listid);
return sts;
}
/* Helper function to check on user buffers */
static BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz,
uint32_t uv_off, bool en_422)
{
if (!ubuff || !ub_sz) {
BCMLOG_ERR("%s->Invalid Arg %p %x\n",
((pin) ? "TX" : "RX"), ubuff, ub_sz);
return BC_STS_INV_ARG;
}
/* Check for alignment */
if (((uintptr_t)ubuff) & 0x03) {
BCMLOG_ERR("%s-->Un-aligned address not implemented yet.. %p \n",
((pin) ? "TX" : "RX"), ubuff);
return BC_STS_NOT_IMPL;
}
if (pin)
return BC_STS_SUCCESS;
if (!en_422 && !uv_off) {
BCMLOG_ERR("Need UV offset for 420 mode.\n");
return BC_STS_INV_ARG;
}
if (en_422 && uv_off) {
BCMLOG_ERR("UV offset in 422 mode ??\n");
return BC_STS_INV_ARG;
}
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
{
void *ubuff;
uint32_t ub_sz;
crystalhd_dio_req *dio_hnd = NULL;
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
ubuff = idata->udata.u.ProcInput.pDmaBuff;
ub_sz = idata->udata.u.ProcInput.BuffSz;
sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0);
if (sts != BC_STS_SUCCESS)
return sts;
sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("dio map - %d \n", sts);
return sts;
}
if (!dio_hnd)
return BC_STS_ERROR;
sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd);
crystalhd_unmap_dio(ctx->adp, dio_hnd);
return sts;
}
static BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
void *ubuff;
uint32_t ub_sz, uv_off;
bool en_422;
crystalhd_dio_req *dio_hnd = NULL;
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
ubuff = idata->udata.u.RxBuffs.YuvBuff;
ub_sz = idata->udata.u.RxBuffs.YuvBuffSz;
uv_off = idata->udata.u.RxBuffs.UVbuffOffset;
en_422 = idata->udata.u.RxBuffs.b422Mode;
sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422);
if (sts != BC_STS_SUCCESS)
return sts;
sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off,
en_422, 0, &dio_hnd);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("dio map - %d \n", sts);
return sts;
}
if (!dio_hnd)
return BC_STS_ERROR;
sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY));
if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) {
crystalhd_unmap_dio(ctx->adp, dio_hnd);
return sts;
}
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx,
crystalhd_dio_req *dio)
{
BC_STATUS sts = BC_STS_SUCCESS;
sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0);
if (sts != BC_STS_SUCCESS)
return sts;
ctx->state |= BC_LINK_FMT_CHG;
if (ctx->state == BC_LINK_READY)
sts = crystalhd_hw_start_capture(&ctx->hw_ctx);
return sts;
}
static BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
crystalhd_dio_req *dio = NULL;
BC_STATUS sts = BC_STS_SUCCESS;
BC_DEC_OUT_BUFF *frame;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
if (!(ctx->state & BC_LINK_CAP_EN)) {
BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state);
return BC_STS_ERR_USAGE;
}
frame = &idata->udata.u.DecOutData;
sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
if (sts != BC_STS_SUCCESS)
return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts;
frame->Flags = dio->uinfo.comp_flags;
if (frame->Flags & COMP_FLAG_FMT_CHANGE)
return bc_cproc_fmt_change(ctx, dio);
frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff;
frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len;
frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset;
frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode;
frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz;
frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz;
crystalhd_unmap_dio(ctx->adp, dio);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
ctx->state |= BC_LINK_CAP_EN;
if (ctx->state == BC_LINK_READY)
return crystalhd_hw_start_capture(&ctx->hw_ctx);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
crystalhd_dio_req *dio = NULL;
BC_STATUS sts = BC_STS_SUCCESS;
BC_DEC_OUT_BUFF *frame;
uint32_t count;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
if (!(ctx->state & BC_LINK_CAP_EN))
return BC_STS_ERR_USAGE;
/* We should ack flush even when we are in paused/suspend state */
if (!(ctx->state & BC_LINK_READY))
return crystalhd_hw_stop_capture(&ctx->hw_ctx);
ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG);
frame = &idata->udata.u.DecOutData;
for (count = 0; count < BC_RX_LIST_CNT; count++) {
sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
if (sts != BC_STS_SUCCESS)
break;
crystalhd_unmap_dio(ctx->adp, dio);
}
return crystalhd_hw_stop_capture(&ctx->hw_ctx);
}
static BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
BC_DTS_STATS *stats;
struct crystalhd_hw_stats hw_stats;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats);
stats = &idata->udata.u.drvStat;
stats->drvRLL = hw_stats.rdyq_count;
stats->drvFLL = hw_stats.freeq_count;
stats->DrvTotalFrmDropped = hw_stats.rx_errors;
stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors;
stats->intCount = hw_stats.num_interrupts;
stats->DrvIgnIntrCnt = hw_stats.num_interrupts -
hw_stats.dev_interrupts;
stats->TxFifoBsyCnt = hw_stats.cin_busy;
stats->pauseCount = hw_stats.pause_cnt;
if (ctx->pwr_state_change)
stats->pwr_state_change = 1;
if (ctx->state & BC_LINK_PAUSED)
stats->DrvPauseTime = 1;
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
crystalhd_hw_stats(&ctx->hw_ctx, NULL);
return BC_STS_SUCCESS;
}
static BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx,
crystalhd_ioctl_data *idata)
{
BC_CLOCK *clock;
uint32_t oldClk;
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Arg!!\n");
return BC_STS_INV_ARG;
}
clock = &idata->udata.u.clockValue;
oldClk = ctx->hw_ctx.core_clock_mhz;
ctx->hw_ctx.core_clock_mhz = clock->clk;
if (ctx->state & BC_LINK_READY) {
sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx);
if (sts == BC_STS_CLK_NOCHG)
ctx->hw_ctx.core_clock_mhz = oldClk;
}
clock->clk = ctx->hw_ctx.core_clock_mhz;
return sts;
}
/*=============== Cmd Proc Table.. ======================================*/
static const crystalhd_cmd_tbl_t g_crystalhd_cproc_tbl[] = {
{ BCM_IOC_GET_VERSION, bc_cproc_get_version, 0},
{ BCM_IOC_GET_HWTYPE, bc_cproc_get_hwtype, 0},
{ BCM_IOC_REG_RD, bc_cproc_reg_rd, 0},
{ BCM_IOC_REG_WR, bc_cproc_reg_wr, 0},
{ BCM_IOC_FPGA_RD, bc_cproc_link_reg_rd, 0},
{ BCM_IOC_FPGA_WR, bc_cproc_link_reg_wr, 0},
{ BCM_IOC_MEM_RD, bc_cproc_mem_rd, 0},
{ BCM_IOC_MEM_WR, bc_cproc_mem_wr, 0},
{ BCM_IOC_RD_PCI_CFG, bc_cproc_cfg_rd, 0},
{ BCM_IOC_WR_PCI_CFG, bc_cproc_cfg_wr, 1},
{ BCM_IOC_FW_DOWNLOAD, bc_cproc_download_fw, 1},
{ BCM_IOC_FW_CMD, bc_cproc_do_fw_cmd, 1},
{ BCM_IOC_PROC_INPUT, bc_cproc_proc_input, 1},
{ BCM_IOC_ADD_RXBUFFS, bc_cproc_add_cap_buff, 1},
{ BCM_IOC_FETCH_RXBUFF, bc_cproc_fetch_frame, 1},
{ BCM_IOC_START_RX_CAP, bc_cproc_start_capture, 1},
{ BCM_IOC_FLUSH_RX_CAP, bc_cproc_flush_cap_buffs, 1},
{ BCM_IOC_GET_DRV_STAT, bc_cproc_get_stats, 0},
{ BCM_IOC_RST_DRV_STAT, bc_cproc_reset_stats, 0},
{ BCM_IOC_NOTIFY_MODE, bc_cproc_notify_mode, 0},
{ BCM_IOC_CHG_CLK, bc_cproc_chg_clk, 0},
{ BCM_IOC_END, NULL},
};
/*=============== Cmd Proc Functions.. ===================================*/
/**
* crystalhd_suspend - Power management suspend request.
* @ctx: Command layer context.
* @idata: Iodata - required for internal use.
*
* Return:
* status
*
* 1. Set the state to Suspend.
* 2. Flush the Rx Buffers it will unmap all the buffers and
* stop the RxDMA engine.
* 3. Cancel The TX Io and Stop Dma Engine.
* 4. Put the DDR in to deep sleep.
* 5. Stop the hardware putting it in to Reset State.
*
* Current gstreamer frame work does not provide any power management
* related notification to user mode decoder plug-in. As a work-around
* we pass on the power mangement notification to our plug-in by completing
* all outstanding requests with BC_STS_IO_USER_ABORT return code.
*/
BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
{
BC_STATUS sts = BC_STS_SUCCESS;
if (!ctx || !idata) {
BCMLOG_ERR("Invalid Parameters\n");
return BC_STS_ERROR;
}
if (ctx->state & BC_LINK_SUSPEND)
return BC_STS_SUCCESS;
if (ctx->state == BC_LINK_INVALID) {
BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
return BC_STS_SUCCESS;
}
ctx->state |= BC_LINK_SUSPEND;
bc_cproc_mark_pwr_state(ctx);
if (ctx->state & BC_LINK_CAP_EN) {
sts = bc_cproc_flush_cap_buffs(ctx, idata);
if (sts != BC_STS_SUCCESS)
return sts;
}
if (ctx->tx_list_id) {
sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
if (sts != BC_STS_SUCCESS)
return sts;
}
sts = crystalhd_hw_suspend(&ctx->hw_ctx);
if (sts != BC_STS_SUCCESS)
return sts;
BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
return BC_STS_SUCCESS;
}
/**
* crystalhd_resume - Resume frame capture.
* @ctx: Command layer contextx.
*
* Return:
* status
*
*
* Resume frame capture.
*
* PM_Resume can't resume the playback state back to pre-suspend state
* because we don't keep video clip related information within driver.
* To get back to the pre-suspend state App will re-open the device and
* start a new playback session from the pre-suspend clip position.
*
*/
BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
{
BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
bc_cproc_mark_pwr_state(ctx);
return BC_STS_SUCCESS;
}
/**
* crystalhd_user_open - Create application handle.
* @ctx: Command layer contextx.
* @user_ctx: User ID context.
*
* Return:
* status
*
* Creates an application specific UID and allocates
* application specific resources. HW layer initialization
* is done for the first open request.
*/
BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
struct crystalhd_user **user_ctx)
{
struct crystalhd_user *uc;
if (!ctx || !user_ctx) {
BCMLOG_ERR("Invalid arg..\n");
return BC_STS_INV_ARG;
}
uc = bc_cproc_get_uid(ctx);
if (!uc) {
BCMLOG(BCMLOG_INFO, "No free user context...\n");
return BC_STS_BUSY;
}
BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
uc->in_use = 1;
*user_ctx = uc;
return BC_STS_SUCCESS;
}
/**
* crystalhd_user_close - Close application handle.
* @ctx: Command layer contextx.
* @uc: User ID context.
*
* Return:
* status
*
* Closer aplication handle and release app specific
* resources.
*/
BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
{
uint32_t mode = uc->mode;
ctx->user[uc->uid].mode = DTS_MODE_INV;
ctx->user[uc->uid].in_use = 0;
ctx->cin_wait_exit = 1;
ctx->pwr_state_change = 0;
BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
crystalhd_destroy_dio_pool(ctx->adp);
} else if (bc_cproc_get_user_count(ctx)) {
return BC_STS_SUCCESS;
}
crystalhd_hw_close(&ctx->hw_ctx);
ctx->state = BC_LINK_INVALID;
return BC_STS_SUCCESS;
}
/**
* crystalhd_setup_cmd_context - Setup Command layer resources.
* @ctx: Command layer contextx.
* @adp: Adapter context
*
* Return:
* status
*
* Called at the time of driver load.
*/
BC_STATUS crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
struct crystalhd_adp *adp)
{
int i = 0;
if (!ctx || !adp) {
BCMLOG_ERR("Invalid arg!!\n");
return BC_STS_INV_ARG;
}
if (ctx->adp)
BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
ctx->adp = adp;
for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
ctx->user[i].uid = i;
ctx->user[i].in_use = 0;
ctx->user[i].mode = DTS_MODE_INV;
}
/*Open and Close the Hardware to put it in to sleep state*/
crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
crystalhd_hw_close(&ctx->hw_ctx);
return BC_STS_SUCCESS;
}
/**
* crystalhd_delete_cmd_context - Release Command layer resources.
* @ctx: Command layer contextx.
*
* Return:
* status
*
* Called at the time of driver un-load.
*/
BC_STATUS crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
{
BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
ctx->adp = NULL;
return BC_STS_SUCCESS;
}
/**
* crystalhd_get_cmd_proc - Cproc table lookup.
* @ctx: Command layer contextx.
* @cmd: IOCTL command code.
* @uc: User ID context.
*
* Return:
* command proc function pointer
*
* This function checks the process context, application's
* mode of operation and returns the function pointer
* from the cproc table.
*/
crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
struct crystalhd_user *uc)
{
crystalhd_cmd_proc cproc = NULL;
unsigned int i, tbl_sz;
if (!ctx) {
BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
return NULL;
}
if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
return NULL;
}
tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(crystalhd_cmd_tbl_t);
for (i = 0; i < tbl_sz; i++) {
if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
if ((uc->mode == DTS_MONITOR_MODE) &&
(g_crystalhd_cproc_tbl[i].block_mon)) {
BCMLOG(BCMLOG_INFO, "Blocking cmd %d \n", cmd);
break;
}
cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
break;
}
}
return cproc;
}
/**
* crystalhd_cmd_interrupt - ISR entry point
* @ctx: Command layer contextx.
*
* Return:
* TRUE: If interrupt from bcm70012 device.
*
*
* ISR entry point from OS layer.
*/
bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
{
if (!ctx) {
BCMLOG_ERR("Invalid arg..\n");
return 0;
}
return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
}
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_cmds . h
*
* Description:
* BCM70010 Linux driver user command interfaces.
*
* 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/>.
**********************************************************************/
#ifndef _CRYSTALHD_CMDS_H_
#define _CRYSTALHD_CMDS_H_
/*
* NOTE:: This is the main interface file between the Linux layer
* and the harware layer. This file will use the definitions
* from _dts_glob and dts_defs etc.. which are defined for
* windows.
*/
#include "crystalhd_misc.h"
#include "crystalhd_hw.h"
enum _crystalhd_state{
BC_LINK_INVALID = 0x00,
BC_LINK_INIT = 0x01,
BC_LINK_CAP_EN = 0x02,
BC_LINK_FMT_CHG = 0x04,
BC_LINK_SUSPEND = 0x10,
BC_LINK_PAUSED = 0x20,
BC_LINK_READY = (BC_LINK_INIT | BC_LINK_CAP_EN | BC_LINK_FMT_CHG),
};
struct crystalhd_user {
uint32_t uid;
uint32_t in_use;
uint32_t mode;
};
#define DTS_MODE_INV (-1)
struct crystalhd_cmd {
uint32_t state;
struct crystalhd_adp *adp;
struct crystalhd_user user[BC_LINK_MAX_OPENS];
spinlock_t ctx_lock;
uint32_t tx_list_id;
uint32_t cin_wait_exit;
uint32_t pwr_state_change;
struct crystalhd_hw hw_ctx;
};
typedef BC_STATUS (*crystalhd_cmd_proc)(struct crystalhd_cmd *, crystalhd_ioctl_data *);
typedef struct _crystalhd_cmd_tbl {
uint32_t cmd_id;
const crystalhd_cmd_proc cmd_proc;
uint32_t block_mon;
} crystalhd_cmd_tbl_t;
BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata);
BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx);
crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
struct crystalhd_user *uc);
BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx, struct crystalhd_user **user_ctx);
BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc);
BC_STATUS crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx, struct crystalhd_adp *adp);
BC_STATUS crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx);
bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx);
#endif
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_fw_if . h
*
* Description:
* BCM70012 Firmware interface definitions.
*
* 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/>.
**********************************************************************/
#ifndef _CRYSTALHD_FW_IF_H_
#define _CRYSTALHD_FW_IF_H_
/* TBD: Pull in only required defs into this file.. */
/* User Data Header */
typedef struct user_data {
struct user_data *next;
uint32_t type;
uint32_t size;
} UD_HDR;
/*------------------------------------------------------*
* MPEG Extension to the PPB *
*------------------------------------------------------*/
typedef struct {
uint32_t to_be_defined;
uint32_t valid;
/* Always valid, defaults to picture size if no
sequence display extension in the stream. */
uint32_t display_horizontal_size;
uint32_t display_vertical_size;
/* MPEG_VALID_PANSCAN
Offsets are a copy values from the MPEG stream. */
uint32_t offset_count;
int32_t horizontal_offset[3];
int32_t vertical_offset[3];
/* MPEG_VALID_USERDATA
User data is in the form of a linked list. */
int32_t userDataSize;
UD_HDR *userData;
} PPB_MPEG;
/*------------------------------------------------------*
* VC1 Extension to the PPB *
*------------------------------------------------------*/
typedef struct {
uint32_t to_be_defined;
uint32_t valid;
/* Always valid, defaults to picture size if no
sequence display extension in the stream. */
uint32_t display_horizontal_size;
uint32_t display_vertical_size;
/* VC1 pan scan windows */
uint32_t num_panscan_windows;
int32_t ps_horiz_offset[4];
int32_t ps_vert_offset[4];
int32_t ps_width[4];
int32_t ps_height[4];
/* VC1_VALID_USERDATA
User data is in the form of a linked list. */
int32_t userDataSize;
UD_HDR *userData;
} PPB_VC1;
/*------------------------------------------------------*
* H.264 Extension to the PPB *
*------------------------------------------------------*/
/**
* @brief Film grain SEI message.
*
* Content of the film grain SEI message.
*/
/* maximum number of model-values as for Thomson spec(standard says 5) */
#define MAX_FGT_MODEL_VALUE (3)
/* maximum number of intervals(as many as 256 intervals?) */
#define MAX_FGT_VALUE_INTERVAL (256)
typedef struct FGT_SEI {
struct FGT_SEI *next;
unsigned char model_values[3][MAX_FGT_VALUE_INTERVAL][MAX_FGT_MODEL_VALUE];
unsigned char upper_bound[3][MAX_FGT_VALUE_INTERVAL];
unsigned char lower_bound[3][MAX_FGT_VALUE_INTERVAL];
unsigned char cancel_flag; /* Cancel flag: 1 no film grain. */
unsigned char model_id; /* Model id. */
/* +unused SE based on Thomson spec */
unsigned char color_desc_flag; /* Separate color descrition flag. */
unsigned char bit_depth_luma; /* Bit depth luma minus 8. */
unsigned char bit_depth_chroma; /* Bit depth chroma minus 8. */
unsigned char full_range_flag; /* Full range flag. */
unsigned char color_primaries; /* Color primaries. */
unsigned char transfer_charact; /* Transfer characteristics. */
unsigned char matrix_coeff; /*< Matrix coefficients. */
/* -unused SE based on Thomson spec */
unsigned char blending_mode_id; /* Blending mode. */
unsigned char log2_scale_factor; /* Log2 scale factor (2-7). */
unsigned char comp_flag[3]; /* Components [0,2] parameters present flag. */
unsigned char num_intervals_minus1[3]; /* Number of intensity level intervals. */
unsigned char num_model_values[3]; /* Number of model values. */
uint16_t repetition_period; /* Repetition period (0-16384) */
} FGT_SEI;
typedef struct {
/* 'valid' specifies which fields (or sets of
* fields) below are valid. If the corresponding
* bit in 'valid' is NOT set then that field(s)
* is (are) not initialized. */
uint32_t valid;
int32_t poc_top; /* POC for Top Field/Frame */
int32_t poc_bottom; /* POC for Bottom Field */
uint32_t idr_pic_id;
/* H264_VALID_PANSCAN */
uint32_t pan_scan_count;
int32_t pan_scan_left[3];
int32_t pan_scan_right[3];
int32_t pan_scan_top[3];
int32_t pan_scan_bottom[3];
/* H264_VALID_CT_TYPE */
uint32_t ct_type_count;
uint32_t ct_type[3];
/* H264_VALID_SPS_CROP */
int32_t sps_crop_left;
int32_t sps_crop_right;
int32_t sps_crop_top;
int32_t sps_crop_bottom;
/* H264_VALID_VUI */
uint32_t chroma_top;
uint32_t chroma_bottom;
/* H264_VALID_USER */
uint32_t user_data_size;
UD_HDR *user_data;
/* H264 VALID FGT */
FGT_SEI *pfgt;
} PPB_H264;
typedef struct {
/* Common fields. */
uint32_t picture_number; /* Ordinal display number */
uint32_t video_buffer; /* Video (picbuf) number */
uint32_t video_address; /* Address of picbuf Y */
uint32_t video_address_uv; /* Address of picbuf UV */
uint32_t video_stripe; /* Picbuf stripe */
uint32_t video_width; /* Picbuf width */
uint32_t video_height; /* Picbuf height */
uint32_t channel_id; /* Decoder channel ID */
uint32_t status; /* reserved */
uint32_t width; /* pixels */
uint32_t height; /* pixels */
uint32_t chroma_format; /* see above */
uint32_t pulldown; /* see above */
uint32_t flags; /* see above */
uint32_t pts; /* 32 LSBs of PTS */
uint32_t protocol; /* protocolXXX (above) */
uint32_t frame_rate; /* see above */
uint32_t matrix_coeff; /* see above */
uint32_t aspect_ratio; /* see above */
uint32_t colour_primaries; /* see above */
uint32_t transfer_char; /* see above */
uint32_t pcr_offset; /* 45kHz if PCR type; else 27MHz */
uint32_t n_drop; /* Number of pictures to be dropped */
uint32_t custom_aspect_ratio_width_height;
/* upper 16-bits is Y and lower 16-bits is X */
uint32_t picture_tag; /* Indexing tag from BUD packets */
uint32_t picture_done_payload;
uint32_t picture_meta_payload;
uint32_t reserved[1];
/* Protocol-specific extensions. */
union {
PPB_H264 h264;
PPB_MPEG mpeg;
PPB_VC1 vc1;
} other;
} PPB;
typedef struct {
uint32_t bFormatChange;
uint32_t resolution;
uint32_t channelId;
uint32_t ppbPtr;
int32_t ptsStcOffset;
uint32_t zeroPanscanValid;
uint32_t dramOutBufAddr;
uint32_t yComponent;
PPB ppb;
} C011_PIB;
typedef struct {
uint32_t command;
uint32_t sequence;
uint32_t status;
uint32_t picBuf;
uint32_t picRelBuf;
uint32_t picInfoDeliveryQ;
uint32_t picInfoReleaseQ;
uint32_t channelStatus;
uint32_t userDataDeliveryQ;
uint32_t userDataReleaseQ;
uint32_t transportStreamCaptureAddr;
uint32_t asyncEventQ;
} DecRspChannelStartVideo;
#define eCMD_C011_CMD_BASE (0x73763000)
/* host commands */
typedef enum {
eCMD_TS_GET_NEXT_PIC = 0x7376F100, /* debug get next picture */
eCMD_TS_GET_LAST_PIC = 0x7376F102, /* debug get last pic status */
eCMD_TS_READ_WRITE_MEM = 0x7376F104, /* debug read write memory */
/* New API commands */
/* General commands */
eCMD_C011_INIT = eCMD_C011_CMD_BASE + 0x01,
eCMD_C011_RESET = eCMD_C011_CMD_BASE + 0x02,
eCMD_C011_SELF_TEST = eCMD_C011_CMD_BASE + 0x03,
eCMD_C011_GET_VERSION = eCMD_C011_CMD_BASE + 0x04,
eCMD_C011_GPIO = eCMD_C011_CMD_BASE + 0x05,
eCMD_C011_DEBUG_SETUP = eCMD_C011_CMD_BASE + 0x06,
/* Decoding commands */
eCMD_C011_DEC_CHAN_OPEN = eCMD_C011_CMD_BASE + 0x100,
eCMD_C011_DEC_CHAN_CLOSE = eCMD_C011_CMD_BASE + 0x101,
eCMD_C011_DEC_CHAN_ACTIVATE = eCMD_C011_CMD_BASE + 0x102,
eCMD_C011_DEC_CHAN_STATUS = eCMD_C011_CMD_BASE + 0x103,
eCMD_C011_DEC_CHAN_FLUSH = eCMD_C011_CMD_BASE + 0x104,
eCMD_C011_DEC_CHAN_TRICK_PLAY = eCMD_C011_CMD_BASE + 0x105,
eCMD_C011_DEC_CHAN_TS_PIDS = eCMD_C011_CMD_BASE + 0x106,
eCMD_C011_DEC_CHAN_PS_STREAM_ID = eCMD_C011_CMD_BASE + 0x107,
eCMD_C011_DEC_CHAN_INPUT_PARAMS = eCMD_C011_CMD_BASE + 0x108,
eCMD_C011_DEC_CHAN_VIDEO_OUTPUT = eCMD_C011_CMD_BASE + 0x109,
eCMD_C011_DEC_CHAN_OUTPUT_FORMAT = eCMD_C011_CMD_BASE + 0x10A,
eCMD_C011_DEC_CHAN_SCALING_FILTERS = eCMD_C011_CMD_BASE + 0x10B,
eCMD_C011_DEC_CHAN_OSD_MODE = eCMD_C011_CMD_BASE + 0x10D,
eCMD_C011_DEC_CHAN_DROP = eCMD_C011_CMD_BASE + 0x10E,
eCMD_C011_DEC_CHAN_RELEASE = eCMD_C011_CMD_BASE + 0x10F,
eCMD_C011_DEC_CHAN_STREAM_SETTINGS = eCMD_C011_CMD_BASE + 0x110,
eCMD_C011_DEC_CHAN_PAUSE_OUTPUT = eCMD_C011_CMD_BASE + 0x111,
eCMD_C011_DEC_CHAN_CHANGE = eCMD_C011_CMD_BASE + 0x112,
eCMD_C011_DEC_CHAN_SET_STC = eCMD_C011_CMD_BASE + 0x113,
eCMD_C011_DEC_CHAN_SET_PTS = eCMD_C011_CMD_BASE + 0x114,
eCMD_C011_DEC_CHAN_CC_MODE = eCMD_C011_CMD_BASE + 0x115,
eCMD_C011_DEC_CREATE_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x116,
eCMD_C011_DEC_COPY_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x117,
eCMD_C011_DEC_DELETE_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x118,
eCMD_C011_DEC_CHAN_SET_DECYPTION = eCMD_C011_CMD_BASE + 0x119,
eCMD_C011_DEC_CHAN_START_VIDEO = eCMD_C011_CMD_BASE + 0x11A,
eCMD_C011_DEC_CHAN_STOP_VIDEO = eCMD_C011_CMD_BASE + 0x11B,
eCMD_C011_DEC_CHAN_PIC_CAPTURE = eCMD_C011_CMD_BASE + 0x11C,
eCMD_C011_DEC_CHAN_PAUSE = eCMD_C011_CMD_BASE + 0x11D,
eCMD_C011_DEC_CHAN_PAUSE_STATE = eCMD_C011_CMD_BASE + 0x11E,
eCMD_C011_DEC_CHAN_SET_SLOWM_RATE = eCMD_C011_CMD_BASE + 0x11F,
eCMD_C011_DEC_CHAN_GET_SLOWM_RATE = eCMD_C011_CMD_BASE + 0x120,
eCMD_C011_DEC_CHAN_SET_FF_RATE = eCMD_C011_CMD_BASE + 0x121,
eCMD_C011_DEC_CHAN_GET_FF_RATE = eCMD_C011_CMD_BASE + 0x122,
eCMD_C011_DEC_CHAN_FRAME_ADVANCE = eCMD_C011_CMD_BASE + 0x123,
eCMD_C011_DEC_CHAN_SET_SKIP_PIC_MODE = eCMD_C011_CMD_BASE + 0x124,
eCMD_C011_DEC_CHAN_GET_SKIP_PIC_MODE = eCMD_C011_CMD_BASE + 0x125,
eCMD_C011_DEC_CHAN_FILL_PIC_BUF = eCMD_C011_CMD_BASE + 0x126,
eCMD_C011_DEC_CHAN_SET_CONTINUITY_CHECK = eCMD_C011_CMD_BASE + 0x127,
eCMD_C011_DEC_CHAN_GET_CONTINUITY_CHECK = eCMD_C011_CMD_BASE + 0x128,
eCMD_C011_DEC_CHAN_SET_BRCM_TRICK_MODE = eCMD_C011_CMD_BASE + 0x129,
eCMD_C011_DEC_CHAN_GET_BRCM_TRICK_MODE = eCMD_C011_CMD_BASE + 0x12A,
eCMD_C011_DEC_CHAN_REVERSE_FIELD_STATUS = eCMD_C011_CMD_BASE + 0x12B,
eCMD_C011_DEC_CHAN_I_PICTURE_FOUND = eCMD_C011_CMD_BASE + 0x12C,
eCMD_C011_DEC_CHAN_SET_PARAMETER = eCMD_C011_CMD_BASE + 0x12D,
eCMD_C011_DEC_CHAN_SET_USER_DATA_MODE = eCMD_C011_CMD_BASE + 0x12E,
eCMD_C011_DEC_CHAN_SET_PAUSE_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x12F,
eCMD_C011_DEC_CHAN_SET_SLOW_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x130,
eCMD_C011_DEC_CHAN_SET_FF_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x131,
eCMD_C011_DEC_CHAN_SET_DISPLAY_TIMING_MODE = eCMD_C011_CMD_BASE + 0x132,
eCMD_C011_DEC_CHAN_SET_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x133,
eCMD_C011_DEC_CHAN_GET_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x134,
eCMD_C011_DEC_CHAN_SET_REVERSE_FIELD = eCMD_C011_CMD_BASE + 0x135,
eCMD_C011_DEC_CHAN_STREAM_OPEN = eCMD_C011_CMD_BASE + 0x136,
eCMD_C011_DEC_CHAN_SET_PCR_PID = eCMD_C011_CMD_BASE + 0x137,
eCMD_C011_DEC_CHAN_SET_VID_PID = eCMD_C011_CMD_BASE + 0x138,
eCMD_C011_DEC_CHAN_SET_PAN_SCAN_MODE = eCMD_C011_CMD_BASE + 0x139,
eCMD_C011_DEC_CHAN_START_DISPLAY_AT_PTS = eCMD_C011_CMD_BASE + 0x140,
eCMD_C011_DEC_CHAN_STOP_DISPLAY_AT_PTS = eCMD_C011_CMD_BASE + 0x141,
eCMD_C011_DEC_CHAN_SET_DISPLAY_ORDER = eCMD_C011_CMD_BASE + 0x142,
eCMD_C011_DEC_CHAN_GET_DISPLAY_ORDER = eCMD_C011_CMD_BASE + 0x143,
eCMD_C011_DEC_CHAN_SET_HOST_TRICK_MODE = eCMD_C011_CMD_BASE + 0x144,
eCMD_C011_DEC_CHAN_SET_OPERATION_MODE = eCMD_C011_CMD_BASE + 0x145,
eCMD_C011_DEC_CHAN_DISPLAY_PAUSE_UNTO_PTS = eCMD_C011_CMD_BASE + 0x146,
eCMD_C011_DEC_CHAN_SET_PTS_STC_DIFF_THRESHOLD = eCMD_C011_CMD_BASE + 0x147,
eCMD_C011_DEC_CHAN_SEND_COMPRESSED_BUF = eCMD_C011_CMD_BASE + 0x148,
eCMD_C011_DEC_CHAN_SET_CLIPPING = eCMD_C011_CMD_BASE + 0x149,
eCMD_C011_DEC_CHAN_SET_PARAMETERS_FOR_HARD_RESET_INTERRUPT_TO_HOST
= eCMD_C011_CMD_BASE + 0x150,
/* Decoder RevD commands */
eCMD_C011_DEC_CHAN_SET_CSC = eCMD_C011_CMD_BASE + 0x180, /* color space conversion */
eCMD_C011_DEC_CHAN_SET_RANGE_REMAP = eCMD_C011_CMD_BASE + 0x181,
eCMD_C011_DEC_CHAN_SET_FGT = eCMD_C011_CMD_BASE + 0x182,
/* Note: 0x183 not implemented yet in Rev D main */
eCMD_C011_DEC_CHAN_SET_LASTPICTURE_PADDING = eCMD_C011_CMD_BASE + 0x183,
/* Decoder 7412 commands (7412-only) */
eCMD_C011_DEC_CHAN_SET_CONTENT_KEY = eCMD_C011_CMD_BASE + 0x190,
eCMD_C011_DEC_CHAN_SET_SESSION_KEY = eCMD_C011_CMD_BASE + 0x191,
eCMD_C011_DEC_CHAN_FMT_CHANGE_ACK = eCMD_C011_CMD_BASE + 0x192,
eCMD_C011_DEC_CHAN_CUSTOM_VIDOUT = eCMD_C011_CMD_BASE + 0x1FF,
/* Encoding commands */
eCMD_C011_ENC_CHAN_OPEN = eCMD_C011_CMD_BASE + 0x200,
eCMD_C011_ENC_CHAN_CLOSE = eCMD_C011_CMD_BASE + 0x201,
eCMD_C011_ENC_CHAN_ACTIVATE = eCMD_C011_CMD_BASE + 0x202,
eCMD_C011_ENC_CHAN_CONTROL = eCMD_C011_CMD_BASE + 0x203,
eCMD_C011_ENC_CHAN_STATISTICS = eCMD_C011_CMD_BASE + 0x204,
eNOTIFY_C011_ENC_CHAN_EVENT = eCMD_C011_CMD_BASE + 0x210,
} eC011_TS_CMD;
#endif
/***************************************************************************
* 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/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)
{
link_misc_perst_deco_ctrl rst_deco_cntrl;
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)
{
link_misc_perst_deco_ctrl rst_deco_cntrl;
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)
{
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)
{
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 crystalhd_rx_dma_pkt *crystalhd_hw_alloc_rx_pkt(struct crystalhd_hw *hw)
{
unsigned long flags = 0;
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,
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;
crystalhd_rx_dma_pkt *pkt = (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 BC_STATUS crystalhd_hw_create_ioqs(struct crystalhd_hw *hw)
{
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 BC_STATUS crystalhd_hw_tx_req_complete(struct crystalhd_hw *hw,
uint32_t list_id, BC_STATUS cs)
{
tx_dma_pkt *tx_req;
if (!hw || !list_id) {
BCMLOG_ERR("Invalid Arg..\n");
return BC_STS_INV_ARG;
}
hw->pwr_lock--;
tx_req = (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(pdma_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 BC_STATUS crystalhd_hw_fill_desc(crystalhd_dio_req *ioreq,
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;
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(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(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 BC_STATUS crystalhd_xlat_sgl_to_dma_desc(crystalhd_dio_req *ioreq,
pdma_desc_mem pdesc_mem,
uint32_t *uv_desc_index)
{
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;
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(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 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(C011_PIB *src_pib, 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;
C011_PIB src_pib;
uint32_t pib_addr, pib_cnt;
BC_PIC_INFO_BLOCK *AppPib;
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(C011_PIB) / 4,
(uint32_t *)&src_pib);
if (src_pib.bFormatChange) {
rx_pkt = (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 BC_STATUS crystalhd_hw_prog_rxdma(struct crystalhd_hw *hw, 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;
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 BC_STATUS crystalhd_hw_post_cap_buff(struct crystalhd_hw *hw,
crystalhd_rx_dma_pkt *rx_pkt)
{
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 BC_STATUS crystalhd_rx_pkt_done(struct crystalhd_hw *hw, uint32_t list_index,
BC_STATUS comp_sts)
{
crystalhd_rx_dma_pkt *rx_pkt = NULL;
uint32_t y_dw_dnsz, uv_dw_dnsz;
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;
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;
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;
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 BC_STATUS crystalhd_fw_cmd_post_proc(struct crystalhd_hw *hw,
BC_FW_CMD *fw_cmd)
{
BC_STATUS sts = BC_STS_SUCCESS;
DecRspChannelStartVideo *st_rsp = NULL;
switch (fw_cmd->cmd[0]) {
case eCMD_C011_DEC_CHAN_START_VIDEO:
st_rsp = (DecRspChannelStartVideo *)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 BC_STATUS crystalhd_put_ddr2sleep(struct crystalhd_hw *hw)
{
uint32_t reg;
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;
}
/************************************************
**
*************************************************/
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;;
}
BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw, 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;
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;
}
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;
}
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;
}
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;
BC_STATUS sts = BC_STS_SUCCESS;
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(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(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(dma_descriptor);
rpkt->pkt_tag = hw->rx_pkt_tag_seed + i;
crystalhd_hw_free_rx_pkt(hw, rpkt);
}
return BC_STS_SUCCESS;
}
BC_STATUS crystalhd_hw_free_dma_rings(struct crystalhd_hw *hw)
{
unsigned int i;
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;
}
BC_STATUS crystalhd_hw_post_tx(struct crystalhd_hw *hw, crystalhd_dio_req *ioreq,
hw_comp_callback call_back,
wait_queue_head_t *cb_event, uint32_t *list_id,
uint8_t data_flags)
{
tx_dma_pkt *tx_dma_packet = NULL;
uint32_t first_desc_u_addr, first_desc_l_addr;
uint32_t low_addr, high_addr;
addr_64 desc_addr;
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 = (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..
*/
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;
}
BC_STATUS crystalhd_hw_add_cap_buffer(struct crystalhd_hw *hw,
crystalhd_dio_req *ioreq, bool en_post)
{
crystalhd_rx_dma_pkt *rpkt;
uint32_t tag, uv_desc_ix = 0;
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(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;
}
BC_STATUS crystalhd_hw_get_cap_buffer(struct crystalhd_hw *hw,
BC_PIC_INFO_BLOCK *pib,
crystalhd_dio_req **ioreq)
{
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;
}
BC_STATUS crystalhd_hw_start_capture(struct crystalhd_hw *hw)
{
crystalhd_rx_dma_pkt *rx_pkt;
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;
}
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;
}
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;
}
BC_STATUS crystalhd_hw_unpause(struct crystalhd_hw *hw)
{
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;
}
BC_STATUS crystalhd_hw_suspend(struct crystalhd_hw *hw)
{
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));
}
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;
}
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_hw . h
*
* Description:
* BCM70012 Linux driver hardware layer.
*
* 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/>.
**********************************************************************/
#ifndef _CRYSTALHD_HW_H_
#define _CRYSTALHD_HW_H_
#include "crystalhd_misc.h"
#include "crystalhd_fw_if.h"
/* HW constants..*/
#define DMA_ENGINE_CNT 2
#define MAX_PIB_Q_DEPTH 64
#define MIN_PIB_Q_DEPTH 2
#define WR_POINTER_OFF 4
#define ASPM_L1_ENABLE (BC_BIT(27))
/*************************************************
7412 Decoder Registers.
**************************************************/
#define FW_CMD_BUFF_SZ 64
#define TS_Host2CpuSnd 0x00000100
#define Hst2CpuMbx1 0x00100F00
#define Cpu2HstMbx1 0x00100F04
#define MbxStat1 0x00100F08
#define Stream2Host_Intr_Sts 0x00100F24
#define C011_RET_SUCCESS 0x0 /* Reutrn status of firmware command. */
/* TS input status register */
#define TS_StreamAFIFOStatus 0x0010044C
#define TS_StreamBFIFOStatus 0x0010084C
/*UART Selection definitions*/
#define UartSelectA 0x00100300
#define UartSelectB 0x00100304
#define BSVS_UART_DEC_NONE 0x00
#define BSVS_UART_DEC_OUTER 0x01
#define BSVS_UART_DEC_INNER 0x02
#define BSVS_UART_STREAM 0x03
/* Code-In fifo */
#define REG_DecCA_RegCinCTL 0xa00
#define REG_DecCA_RegCinBase 0xa0c
#define REG_DecCA_RegCinEnd 0xa10
#define REG_DecCA_RegCinWrPtr 0xa04
#define REG_DecCA_RegCinRdPtr 0xa08
#define REG_Dec_TsUser0Base 0x100864
#define REG_Dec_TsUser0Rdptr 0x100868
#define REG_Dec_TsUser0Wrptr 0x10086C
#define REG_Dec_TsUser0End 0x100874
/* ASF Case ...*/
#define REG_Dec_TsAudCDB2Base 0x10036c
#define REG_Dec_TsAudCDB2Rdptr 0x100378
#define REG_Dec_TsAudCDB2Wrptr 0x100374
#define REG_Dec_TsAudCDB2End 0x100370
/* DRAM bringup Registers */
#define SDRAM_PARAM 0x00040804
#define SDRAM_PRECHARGE 0x000408B0
#define SDRAM_EXT_MODE 0x000408A4
#define SDRAM_MODE 0x000408A0
#define SDRAM_REFRESH 0x00040890
#define SDRAM_REF_PARAM 0x00040808
#define DecHt_PllACtl 0x34000C
#define DecHt_PllBCtl 0x340010
#define DecHt_PllCCtl 0x340014
#define DecHt_PllDCtl 0x340034
#define DecHt_PllECtl 0x340038
#define AUD_DSP_MISC_SOFT_RESET 0x00240104
#define AIO_MISC_PLL_RESET 0x0026000C
#define PCIE_CLK_REQ_REG 0xDC
#define PCI_CLK_REQ_ENABLE (BC_BIT(8))
/*************************************************
F/W Copy engine definitions..
**************************************************/
#define BC_FWIMG_ST_ADDR 0x00000000
/* FIXME: jarod: there's a kernel function that'll do this for us... */
#define rotr32_1(x, n) (((x) >> n) | ((x) << (32 - n)))
#define bswap_32_1(x) ((rotr32_1((x), 24) & 0x00ff00ff) | (rotr32_1((x), 8) & 0xff00ff00))
#define DecHt_HostSwReset 0x340000
#define BC_DRAM_FW_CFG_ADDR 0x001c2000
typedef union _addr_64_ {
struct {
uint32_t low_part;
uint32_t high_part;
};
uint64_t full_addr;
} addr_64;
typedef union _intr_mask_reg_ {
struct {
uint32_t mask_tx_done:1;
uint32_t mask_tx_err:1;
uint32_t mask_rx_done:1;
uint32_t mask_rx_err:1;
uint32_t mask_pcie_err:1;
uint32_t mask_pcie_rbusmast_err:1;
uint32_t mask_pcie_rgr_bridge:1;
uint32_t reserved:25;
};
uint32_t whole_reg;
} intr_mask_reg;
typedef union _link_misc_perst_deco_ctrl_ {
struct {
uint32_t bcm7412_rst:1; /* 1 -> BCM7412 is held in reset. Reset value 1.*/
uint32_t reserved0:3; /* Reserved.No Effect*/
uint32_t stop_bcm_7412_clk:1; /* 1 ->Stops branch of 27MHz clk used to clk BCM7412*/
uint32_t reserved1:27; /* Reseved. No Effect*/
};
uint32_t whole_reg;
} link_misc_perst_deco_ctrl;
typedef union _link_misc_perst_clk_ctrl_ {
struct {
uint32_t sel_alt_clk:1; /* When set, selects a 6.75MHz clock as the source of core_clk */
uint32_t stop_core_clk:1; /* When set, stops the branch of core_clk that is not needed for low power operation */
uint32_t pll_pwr_dn:1; /* When set, powers down the main PLL. The alternate clock bit should be set
to select an alternate clock before setting this bit.*/
uint32_t reserved0:5; /* Reserved */
uint32_t pll_mult:8; /* This setting controls the multiplier for the PLL. */
uint32_t pll_div:4; /* This setting controls the divider for the PLL. */
uint32_t reserved1:12; /* Reserved */
};
uint32_t whole_reg;
} link_misc_perst_clk_ctrl;
typedef union _link_misc_perst_decoder_ctrl_ {
struct {
uint32_t bcm_7412_rst:1; /* 1 -> BCM7412 is held in reset. Reset value 1.*/
uint32_t res0:3; /* Reserved.No Effect*/
uint32_t stop_7412_clk:1; /* 1 ->Stops branch of 27MHz clk used to clk BCM7412*/
uint32_t res1:27; /* Reseved. No Effect */
};
uint32_t whole_reg;
} link_misc_perst_decoder_ctrl;
typedef union _desc_low_addr_reg_ {
struct {
uint32_t list_valid:1;
uint32_t reserved:4;
uint32_t low_addr:27;
};
uint32_t whole_reg;
} desc_low_addr_reg;
typedef struct _dma_descriptor_ { /* 8 32-bit values */
/* 0th u32 */
uint32_t sdram_buff_addr:28; /* bits 0-27: SDRAM Address */
uint32_t res0:4; /* bits 28-31: Reserved */
/* 1st u32 */
uint32_t buff_addr_low; /* 1 buffer address low */
uint32_t buff_addr_high; /* 2 buffer address high */
/* 3rd u32 */
uint32_t res2:2; /* 0-1 - Reserved */
uint32_t xfer_size:23; /* 2-24 = Xfer size in words */
uint32_t res3:6; /* 25-30 reserved */
uint32_t intr_enable:1; /* 31 - Interrupt After this desc */
/* 4th u32 */
uint32_t endian_xlat_align:2; /* 0-1 Endian Translation */
uint32_t next_desc_cont:1; /* 2 - Next desc is in contig memory */
uint32_t res4:25; /* 3 - 27 Reserved bits */
uint32_t fill_bytes:2; /* 28-29 Bits Fill Bytes */
uint32_t dma_dir:1; /* 30 bit DMA Direction */
uint32_t last_rec_indicator:1; /* 31 bit Last Record Indicator */
/* 5th u32 */
uint32_t next_desc_addr_low; /* 32-bits Next Desc Addr lower */
/* 6th u32 */
uint32_t next_desc_addr_high; /* 32-bits Next Desc Addr Higher */
/* 7th u32 */
uint32_t res8; /* Last 32bits reserved */
} dma_descriptor, *pdma_descriptor;
/*
* We will allocate the memory in 4K pages
* the linked list will be a list of 32 byte descriptors.
* The virtual address will determine what should be freed.
*/
typedef struct _dma_desc_mem_ {
pdma_descriptor pdma_desc_start; /* 32-bytes for dma descriptor. should be first element */
dma_addr_t phy_addr; /* physical address of each DMA desc */
uint32_t sz;
struct _dma_desc_mem_ *Next; /* points to Next Descriptor in chain */
} dma_desc_mem, *pdma_desc_mem;
typedef enum _list_sts_ {
sts_free = 0,
/* RX-Y Bits 0:7 */
rx_waiting_y_intr = 0x00000001,
rx_y_error = 0x00000004,
/* RX-UV Bits 8:16 */
rx_waiting_uv_intr = 0x0000100,
rx_uv_error = 0x0000400,
rx_sts_waiting = (rx_waiting_y_intr|rx_waiting_uv_intr),
rx_sts_error = (rx_y_error|rx_uv_error),
rx_y_mask = 0x000000FF,
rx_uv_mask = 0x0000FF00,
} list_sts;
typedef struct _tx_dma_pkt_ {
dma_desc_mem desc_mem;
hw_comp_callback call_back;
crystalhd_dio_req *dio_req;
wait_queue_head_t *cb_event;
uint32_t list_tag;
} tx_dma_pkt;
typedef struct _crystalhd_rx_dma_pkt {
dma_desc_mem desc_mem;
crystalhd_dio_req *dio_req;
uint32_t pkt_tag;
uint32_t flags;
BC_PIC_INFO_BLOCK pib;
dma_addr_t uv_phy_addr;
struct _crystalhd_rx_dma_pkt *next;
} crystalhd_rx_dma_pkt;
struct crystalhd_hw_stats{
uint32_t rx_errors;
uint32_t tx_errors;
uint32_t freeq_count;
uint32_t rdyq_count;
uint32_t num_interrupts;
uint32_t dev_interrupts;
uint32_t cin_busy;
uint32_t pause_cnt;
};
struct crystalhd_hw {
tx_dma_pkt tx_pkt_pool[DMA_ENGINE_CNT];
spinlock_t lock;
uint32_t tx_ioq_tag_seed;
uint32_t tx_list_post_index;
crystalhd_rx_dma_pkt *rx_pkt_pool_head;
uint32_t rx_pkt_tag_seed;
bool dev_started;
void *adp;
wait_queue_head_t *pfw_cmd_event;
int fwcmd_evt_sts;
uint32_t pib_del_Q_addr;
uint32_t pib_rel_Q_addr;
crystalhd_dioq_t *tx_freeq;
crystalhd_dioq_t *tx_actq;
/* Rx DMA Engine Specific Locks */
spinlock_t rx_lock;
uint32_t rx_list_post_index;
list_sts rx_list_sts[DMA_ENGINE_CNT];
crystalhd_dioq_t *rx_rdyq;
crystalhd_dioq_t *rx_freeq;
crystalhd_dioq_t *rx_actq;
uint32_t stop_pending;
/* HW counters.. */
struct crystalhd_hw_stats stats;
/* Core clock in MHz */
uint32_t core_clock_mhz;
uint32_t prev_n;
uint32_t pwr_lock;
};
/* Clock defines for power control */
#define CLOCK_PRESET 175
/* DMA engine register BIT mask wrappers.. */
#define DMA_START_BIT MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_MASK
#define GET_RX_INTR_MASK (INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_MASK | \
INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_MASK | \
INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_MASK | \
INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_MASK | \
INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_MASK | \
INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_MASK | \
INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_MASK | \
INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_MASK)
#define GET_Y0_ERR_MSK (MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK)
#define GET_UV0_ERR_MSK (MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK)
#define GET_Y1_ERR_MSK (MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK | \
MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK)
#define GET_UV1_ERR_MSK (MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK | \
MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK)
/**** API Exposed to the other layers ****/
BC_STATUS crystalhd_download_fw(struct crystalhd_adp *adp,
void *buffer, uint32_t sz);
BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw, BC_FW_CMD *fw_cmd);
bool crystalhd_hw_interrupt(struct crystalhd_adp *adp, struct crystalhd_hw *hw);
BC_STATUS crystalhd_hw_open(struct crystalhd_hw *, struct crystalhd_adp *);
BC_STATUS crystalhd_hw_close(struct crystalhd_hw *);
BC_STATUS crystalhd_hw_setup_dma_rings(struct crystalhd_hw *);
BC_STATUS crystalhd_hw_free_dma_rings(struct crystalhd_hw *);
BC_STATUS crystalhd_hw_post_tx(struct crystalhd_hw *hw, crystalhd_dio_req *ioreq,
hw_comp_callback call_back,
wait_queue_head_t *cb_event,
uint32_t *list_id, uint8_t data_flags);
BC_STATUS crystalhd_hw_pause(struct crystalhd_hw *hw);
BC_STATUS crystalhd_hw_unpause(struct crystalhd_hw *hw);
BC_STATUS crystalhd_hw_suspend(struct crystalhd_hw *hw);
BC_STATUS crystalhd_hw_cancel_tx(struct crystalhd_hw *hw, uint32_t list_id);
BC_STATUS crystalhd_hw_add_cap_buffer(struct crystalhd_hw *hw,
crystalhd_dio_req *ioreq, bool en_post);
BC_STATUS crystalhd_hw_get_cap_buffer(struct crystalhd_hw *hw,
BC_PIC_INFO_BLOCK *pib,
crystalhd_dio_req **ioreq);
BC_STATUS crystalhd_hw_stop_capture(struct crystalhd_hw *hw);
BC_STATUS crystalhd_hw_start_capture(struct crystalhd_hw *hw);
void crystalhd_hw_stats(struct crystalhd_hw *hw, struct crystalhd_hw_stats *stats);
/* API to program the core clock on the decoder */
BC_STATUS crystalhd_hw_set_core_clock(struct crystalhd_hw *);
#endif
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_lnx . c
*
* Description:
* BCM70010 Linux driver
*
* 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 <linux/version.h>
#include "crystalhd_lnx.h"
static struct class *crystalhd_class;
static struct crystalhd_adp *g_adp_info;
static irqreturn_t chd_dec_isr(int irq, void *arg)
{
struct crystalhd_adp *adp = (struct crystalhd_adp *) arg;
int rc = 0;
if (adp)
rc = crystalhd_cmd_interrupt(&adp->cmds);
return IRQ_RETVAL(rc);
}
static int chd_dec_enable_int(struct crystalhd_adp *adp)
{
int rc = 0;
if (!adp || !adp->pdev) {
BCMLOG_ERR("Invalid arg!!\n");
return -EINVAL;
}
if (adp->pdev->msi_enabled)
adp->msi = 1;
else
adp->msi = pci_enable_msi(adp->pdev);
rc = request_irq(adp->pdev->irq, chd_dec_isr, IRQF_SHARED,
adp->name, (void *)adp);
if (rc) {
BCMLOG_ERR("Interrupt request failed.. \n");
pci_disable_msi(adp->pdev);
}
return rc;
}
static int chd_dec_disable_int(struct crystalhd_adp *adp)
{
if (!adp || !adp->pdev) {
BCMLOG_ERR("Invalid arg!!\n");
return -EINVAL;
}
free_irq(adp->pdev->irq, adp);
if (adp->msi)
pci_disable_msi(adp->pdev);
return 0;
}
crystalhd_ioctl_data *chd_dec_alloc_iodata(struct crystalhd_adp *adp, bool isr)
{
unsigned long flags = 0;
crystalhd_ioctl_data *temp;
if (!adp)
return NULL;
spin_lock_irqsave(&adp->lock, flags);
temp = adp->idata_free_head;
if (temp) {
adp->idata_free_head = adp->idata_free_head->next;
memset(temp, 0, sizeof(*temp));
}
spin_unlock_irqrestore(&adp->lock, flags);
return temp;
}
void chd_dec_free_iodata(struct crystalhd_adp *adp, crystalhd_ioctl_data *iodata,
bool isr)
{
unsigned long flags = 0;
if (!adp || !iodata)
return;
spin_lock_irqsave(&adp->lock, flags);
iodata->next = adp->idata_free_head;
adp->idata_free_head = iodata;
spin_unlock_irqrestore(&adp->lock, flags);
}
static inline int crystalhd_user_data(unsigned long ud, void *dr, int size, int set)
{
int rc;
if (!ud || !dr) {
BCMLOG_ERR("Invalid arg \n");
return -EINVAL;
}
if (set)
rc = copy_to_user((void *)ud, dr, size);
else
rc = copy_from_user(dr, (void *)ud, size);
if (rc) {
BCMLOG_ERR("Invalid args for command \n");
rc = -EFAULT;
}
return rc;
}
static int chd_dec_fetch_cdata(struct crystalhd_adp *adp, crystalhd_ioctl_data *io,
uint32_t m_sz, unsigned long ua)
{
unsigned long ua_off;
int rc = 0;
if (!adp || !io || !ua || !m_sz) {
BCMLOG_ERR("Invalid Arg!!\n");
return -EINVAL;
}
io->add_cdata = vmalloc(m_sz);
if (!io->add_cdata) {
BCMLOG_ERR("kalloc fail for sz:%x\n", m_sz);
return -ENOMEM;
}
io->add_cdata_sz = m_sz;
ua_off = ua + sizeof(io->udata);
rc = crystalhd_user_data(ua_off, io->add_cdata, io->add_cdata_sz, 0);
if (rc) {
BCMLOG_ERR("failed to pull add_cdata sz:%x ua_off:%x\n",
io->add_cdata_sz, (unsigned int)ua_off);
if (io->add_cdata) {
kfree(io->add_cdata);
io->add_cdata = NULL;
}
return -ENODATA;
}
return rc;
}
static int chd_dec_release_cdata(struct crystalhd_adp *adp,
crystalhd_ioctl_data *io, unsigned long ua)
{
unsigned long ua_off;
int rc;
if (!adp || !io || !ua) {
BCMLOG_ERR("Invalid Arg!!\n");
return -EINVAL;
}
if (io->cmd != BCM_IOC_FW_DOWNLOAD) {
ua_off = ua + sizeof(io->udata);
rc = crystalhd_user_data(ua_off, io->add_cdata,
io->add_cdata_sz, 1);
if (rc) {
BCMLOG_ERR("failed to push add_cdata sz:%x ua_off:%x\n",
io->add_cdata_sz, (unsigned int)ua_off);
return -ENODATA;
}
}
if (io->add_cdata) {
vfree(io->add_cdata);
io->add_cdata = NULL;
}
return 0;
}
static int chd_dec_proc_user_data(struct crystalhd_adp *adp,
crystalhd_ioctl_data *io,
unsigned long ua, int set)
{
int rc;
uint32_t m_sz = 0;
if (!adp || !io || !ua) {
BCMLOG_ERR("Invalid Arg!!\n");
return -EINVAL;
}
rc = crystalhd_user_data(ua, &io->udata, sizeof(io->udata), set);
if (rc) {
BCMLOG_ERR("failed to %s iodata \n", (set ? "set" : "get"));
return rc;
}
switch (io->cmd) {
case BCM_IOC_MEM_RD:
case BCM_IOC_MEM_WR:
case BCM_IOC_FW_DOWNLOAD:
m_sz = io->udata.u.devMem.NumDwords * 4;
if (set)
rc = chd_dec_release_cdata(adp, io, ua);
else
rc = chd_dec_fetch_cdata(adp, io, m_sz, ua);
break;
default:
break;
}
return rc;
}
static int chd_dec_api_cmd(struct crystalhd_adp *adp, unsigned long ua,
uint32_t uid, uint32_t cmd, crystalhd_cmd_proc func)
{
int rc;
crystalhd_ioctl_data *temp;
BC_STATUS sts = BC_STS_SUCCESS;
temp = chd_dec_alloc_iodata(adp, 0);
if (!temp) {
BCMLOG_ERR("Failed to get iodata..\n");
return -EINVAL;
}
temp->u_id = uid;
temp->cmd = cmd;
rc = chd_dec_proc_user_data(adp, temp, ua, 0);
if (!rc) {
sts = func(&adp->cmds, temp);
if (sts == BC_STS_PENDING)
sts = BC_STS_NOT_IMPL;
temp->udata.RetSts = sts;
rc = chd_dec_proc_user_data(adp, temp, ua, 1);
}
if (temp) {
chd_dec_free_iodata(adp, temp, 0);
temp = NULL;
}
return rc;
}
/* ========================= API interfaces =================================*/
static int chd_dec_ioctl(struct inode *in, struct file *fd,
unsigned int cmd, unsigned long ua)
{
struct crystalhd_adp *adp = chd_get_adp();
crystalhd_cmd_proc cproc;
struct crystalhd_user *uc;
if (!adp || !fd) {
BCMLOG_ERR("Invalid adp\n");
return -EINVAL;
}
uc = (struct crystalhd_user *)fd->private_data;
if (!uc) {
BCMLOG_ERR("Failed to get uc\n");
return -ENODATA;
}
cproc = crystalhd_get_cmd_proc(&adp->cmds, cmd, uc);
if (!cproc) {
BCMLOG_ERR("Unhandled command: %d\n", cmd);
return -EINVAL;
}
return chd_dec_api_cmd(adp, ua, uc->uid, cmd, cproc);
}
static int chd_dec_open(struct inode *in, struct file *fd)
{
struct crystalhd_adp *adp = chd_get_adp();
int rc = 0;
BC_STATUS sts = BC_STS_SUCCESS;
struct crystalhd_user *uc = NULL;
BCMLOG_ENTER;
if (!adp) {
BCMLOG_ERR("Invalid adp\n");
return -EINVAL;
}
if (adp->cfg_users >= BC_LINK_MAX_OPENS) {
BCMLOG(BCMLOG_INFO, "Already in use.%d\n", adp->cfg_users);
return -EBUSY;
}
sts = crystalhd_user_open(&adp->cmds, &uc);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("cmd_user_open - %d \n", sts);
rc = -EBUSY;
}
adp->cfg_users++;
fd->private_data = uc;
return rc;
}
static int chd_dec_close(struct inode *in, struct file *fd)
{
struct crystalhd_adp *adp = chd_get_adp();
struct crystalhd_user *uc;
BCMLOG_ENTER;
if (!adp) {
BCMLOG_ERR("Invalid adp \n");
return -EINVAL;
}
uc = (struct crystalhd_user *)fd->private_data;
if (!uc) {
BCMLOG_ERR("Failed to get uc\n");
return -ENODATA;
}
crystalhd_user_close(&adp->cmds, uc);
adp->cfg_users--;
return 0;
}
static const struct file_operations chd_dec_fops = {
.owner = THIS_MODULE,
.ioctl = chd_dec_ioctl,
.open = chd_dec_open,
.release = chd_dec_close,
};
static int chd_dec_init_chdev(struct crystalhd_adp *adp)
{
crystalhd_ioctl_data *temp;
struct device *dev;
int rc = -ENODEV, i = 0;
if (!adp)
goto fail;
adp->chd_dec_major = register_chrdev(0, CRYSTALHD_API_NAME,
&chd_dec_fops);
if (adp->chd_dec_major < 0) {
BCMLOG_ERR("Failed to create config dev\n");
rc = adp->chd_dec_major;
goto fail;
}
/* register crystalhd class */
crystalhd_class = class_create(THIS_MODULE, "crystalhd");
if (IS_ERR(crystalhd_class)) {
BCMLOG_ERR("failed to create class\n");
goto fail;
}
dev = device_create(crystalhd_class, NULL, MKDEV(adp->chd_dec_major, 0),
NULL, "crystalhd");
if (!dev) {
BCMLOG_ERR("failed to create device\n");
goto device_create_fail;
}
rc = crystalhd_create_elem_pool(adp, BC_LINK_ELEM_POOL_SZ);
if (rc) {
BCMLOG_ERR("failed to create device\n");
goto elem_pool_fail;
}
/* Allocate general purpose ioctl pool. */
for (i = 0; i < CHD_IODATA_POOL_SZ; i++) {
/* FIXME: jarod: why atomic? */
temp = kzalloc(sizeof(crystalhd_ioctl_data), GFP_ATOMIC);
if (!temp) {
BCMLOG_ERR("ioctl data pool kzalloc failed\n");
rc = -ENOMEM;
goto kzalloc_fail;
}
/* Add to global pool.. */
chd_dec_free_iodata(adp, temp, 0);
}
return 0;
kzalloc_fail:
crystalhd_delete_elem_pool(adp);
elem_pool_fail:
device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
device_create_fail:
class_destroy(crystalhd_class);
fail:
return rc;
}
static void chd_dec_release_chdev(struct crystalhd_adp *adp)
{
crystalhd_ioctl_data *temp = NULL;
if (!adp)
return;
if (adp->chd_dec_major > 0) {
/* unregister crystalhd class */
device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
unregister_chrdev(adp->chd_dec_major, CRYSTALHD_API_NAME);
BCMLOG(BCMLOG_INFO, "released api device - %d\n",
adp->chd_dec_major);
class_destroy(crystalhd_class);
}
adp->chd_dec_major = 0;
/* Clear iodata pool.. */
do {
temp = chd_dec_alloc_iodata(adp, 0);
if (temp)
kfree(temp);
} while (temp);
crystalhd_delete_elem_pool(adp);
}
static int chd_pci_reserve_mem(struct crystalhd_adp *pinfo)
{
int rc;
unsigned long bar2 = pci_resource_start(pinfo->pdev, 2);
uint32_t mem_len = pci_resource_len(pinfo->pdev, 2);
unsigned long bar0 = pci_resource_start(pinfo->pdev, 0);
uint32_t i2o_len = pci_resource_len(pinfo->pdev, 0);
BCMLOG(BCMLOG_SSTEP, "bar2:0x%lx-0x%08x bar0:0x%lx-0x%08x\n",
bar2, mem_len, bar0, i2o_len);
rc = check_mem_region(bar2, mem_len);
if (rc) {
BCMLOG_ERR("No valid mem region...\n");
return -ENOMEM;
}
pinfo->addr = ioremap_nocache(bar2, mem_len);
if (!pinfo->addr) {
BCMLOG_ERR("Failed to remap mem region...\n");
return -ENOMEM;
}
pinfo->pci_mem_start = bar2;
pinfo->pci_mem_len = mem_len;
rc = check_mem_region(bar0, i2o_len);
if (rc) {
BCMLOG_ERR("No valid mem region...\n");
return -ENOMEM;
}
pinfo->i2o_addr = ioremap_nocache(bar0, i2o_len);
if (!pinfo->i2o_addr) {
BCMLOG_ERR("Failed to remap mem region...\n");
return -ENOMEM;
}
pinfo->pci_i2o_start = bar0;
pinfo->pci_i2o_len = i2o_len;
rc = pci_request_regions(pinfo->pdev, pinfo->name);
if (rc < 0) {
BCMLOG_ERR("Region request failed: %d\n", rc);
return rc;
}
BCMLOG(BCMLOG_SSTEP, "Mapped addr:0x%08lx i2o_addr:0x%08lx\n",
(unsigned long)pinfo->addr, (unsigned long)pinfo->i2o_addr);
return 0;
}
static void chd_pci_release_mem(struct crystalhd_adp *pinfo)
{
if (!pinfo)
return;
if (pinfo->addr)
iounmap(pinfo->addr);
if (pinfo->i2o_addr)
iounmap(pinfo->i2o_addr);
pci_release_regions(pinfo->pdev);
}
static void chd_dec_pci_remove(struct pci_dev *pdev)
{
struct crystalhd_adp *pinfo;
BC_STATUS sts = BC_STS_SUCCESS;
BCMLOG_ENTER;
pinfo = (struct crystalhd_adp *) pci_get_drvdata(pdev);
if (!pinfo) {
BCMLOG_ERR("could not get adp\n");
return;
}
sts = crystalhd_delete_cmd_context(&pinfo->cmds);
if (sts != BC_STS_SUCCESS)
BCMLOG_ERR("cmd delete :%d \n", sts);
chd_dec_release_chdev(pinfo);
chd_dec_disable_int(pinfo);
chd_pci_release_mem(pinfo);
pci_disable_device(pinfo->pdev);
kfree(pinfo);
g_adp_info = NULL;
}
static int chd_dec_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *entry)
{
struct crystalhd_adp *pinfo;
int rc;
BC_STATUS sts = BC_STS_SUCCESS;
BCMLOG(BCMLOG_DBG, "PCI_INFO: Vendor:0x%04x Device:0x%04x "
"s_vendor:0x%04x s_device: 0x%04x\n",
pdev->vendor, pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device);
/* FIXME: jarod: why atomic? */
pinfo = kzalloc(sizeof(struct crystalhd_adp), GFP_ATOMIC);
if (!pinfo) {
BCMLOG_ERR("Failed to allocate memory\n");
return -ENOMEM;
}
pinfo->pdev = pdev;
rc = pci_enable_device(pdev);
if (rc) {
BCMLOG_ERR("Failed to enable PCI device\n");
return rc;
}
snprintf(pinfo->name, 31, "crystalhd_pci_e:%d:%d:%d",
pdev->bus->number, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn));
rc = chd_pci_reserve_mem(pinfo);
if (rc) {
BCMLOG_ERR("Failed to setup memory regions.\n");
return -ENOMEM;
}
pinfo->present = 1;
pinfo->drv_data = entry->driver_data;
/* Setup adapter level lock.. */
spin_lock_init(&pinfo->lock);
/* setup api stuff.. */
chd_dec_init_chdev(pinfo);
rc = chd_dec_enable_int(pinfo);
if (rc) {
BCMLOG_ERR("_enable_int err:%d \n", rc);
pci_disable_device(pdev);
return -ENODEV;
}
/* Set dma mask... */
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
pinfo->dmabits = 64;
} else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
pinfo->dmabits = 32;
} else {
BCMLOG_ERR("Unabled to setup DMA %d\n", rc);
pci_disable_device(pdev);
return -ENODEV;
}
sts = crystalhd_setup_cmd_context(&pinfo->cmds, pinfo);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("cmd setup :%d \n", sts);
pci_disable_device(pdev);
return -ENODEV;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, pinfo);
g_adp_info = pinfo;
return 0;
}
#ifdef CONFIG_PM
int chd_dec_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct crystalhd_adp *adp;
crystalhd_ioctl_data *temp;
BC_STATUS sts = BC_STS_SUCCESS;
adp = (struct crystalhd_adp *)pci_get_drvdata(pdev);
if (!adp) {
BCMLOG_ERR("could not get adp\n");
return -ENODEV;
}
temp = chd_dec_alloc_iodata(adp, false);
if (!temp) {
BCMLOG_ERR("could not get ioctl data\n");
return -ENODEV;
}
sts = crystalhd_suspend(&adp->cmds, temp);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("BCM70012 Suspend %d\n", sts);
return -ENODEV;
}
chd_dec_free_iodata(adp, temp, false);
chd_dec_disable_int(adp);
pci_save_state(pdev);
/* Disable IO/bus master/irq router */
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
int chd_dec_pci_resume(struct pci_dev *pdev)
{
struct crystalhd_adp *adp;
BC_STATUS sts = BC_STS_SUCCESS;
int rc;
adp = (struct crystalhd_adp *)pci_get_drvdata(pdev);
if (!adp) {
BCMLOG_ERR("could not get adp\n");
return -ENODEV;
}
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/* device's irq possibly is changed, driver should take care */
if (pci_enable_device(pdev)) {
BCMLOG_ERR("Failed to enable PCI device\n");
return 1;
}
pci_set_master(pdev);
rc = chd_dec_enable_int(adp);
if (rc) {
BCMLOG_ERR("_enable_int err:%d \n", rc);
pci_disable_device(pdev);
return -ENODEV;
}
sts = crystalhd_resume(&adp->cmds);
if (sts != BC_STS_SUCCESS) {
BCMLOG_ERR("BCM70012 Resume %d\n", sts);
pci_disable_device(pdev);
return -ENODEV;
}
return 0;
}
#endif
static struct pci_device_id chd_dec_pci_id_table[] = {
/* vendor, device, subvendor, subdevice, class, classmask, driver_data */
{ 0x14e4, 0x1612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 },
{ 0, },
};
struct pci_driver bc_chd_70012_driver = {
.name = "Broadcom 70012 Decoder",
.probe = chd_dec_pci_probe,
.remove = chd_dec_pci_remove,
.id_table = chd_dec_pci_id_table,
#ifdef CONFIG_PM
.suspend = chd_dec_pci_suspend,
.resume = chd_dec_pci_resume
#endif
};
MODULE_DEVICE_TABLE(pci, chd_dec_pci_id_table);
void chd_set_log_level(struct crystalhd_adp *adp, char *arg)
{
if ((!arg) || (strlen(arg) < 3))
g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA;
else if (!strncmp(arg, "sstep", 5))
g_linklog_level = BCMLOG_INFO | BCMLOG_DATA | BCMLOG_DBG |
BCMLOG_SSTEP | BCMLOG_ERROR;
else if (!strncmp(arg, "info", 4))
g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO;
else if (!strncmp(arg, "debug", 5))
g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO |
BCMLOG_DBG;
else if (!strncmp(arg, "pball", 5))
g_linklog_level = 0xFFFFFFFF & ~(BCMLOG_SPINLOCK);
else if (!strncmp(arg, "silent", 6))
g_linklog_level = 0;
else
g_linklog_level = 0;
}
struct crystalhd_adp *chd_get_adp(void)
{
return g_adp_info;
}
int __init chd_dec_module_init(void)
{
int rc;
chd_set_log_level(NULL, "debug");
BCMLOG(BCMLOG_DATA, "Loading crystalhd %d.%d.%d \n",
crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
rc = pci_register_driver(&bc_chd_70012_driver);
if (rc < 0)
BCMLOG_ERR("Could not find any devices. err:%d \n", rc);
return rc;
}
void __exit chd_dec_module_cleanup(void)
{
BCMLOG(BCMLOG_DATA, "unloading crystalhd %d.%d.%d \n",
crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
pci_unregister_driver(&bc_chd_70012_driver);
}
MODULE_AUTHOR("Naren Sankar <nsankar@broadcom.com>");
MODULE_AUTHOR("Prasad Bolisetty <prasadb@broadcom.com>");
MODULE_DESCRIPTION(CRYSTAL_HD_NAME);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bcm70012");
module_init(chd_dec_module_init);
module_exit(chd_dec_module_cleanup);
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_lnx . c
*
* Description:
* BCM70012 Linux driver
*
* 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/>.
**********************************************************************/
#ifndef _CRYSTALHD_LNX_H_
#define _CRYSTALHD_LNX_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pagemap.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "crystalhd_cmds.h"
#define CRYSTAL_HD_NAME "Broadcom Crystal HD Decoder (BCM70012) Driver"
/* OS specific PCI information structure and adapter information. */
struct crystalhd_adp {
/* Hardware borad/PCI specifics */
char name[32];
struct pci_dev *pdev;
unsigned long pci_mem_start;
uint32_t pci_mem_len;
void *addr;
unsigned long pci_i2o_start;
uint32_t pci_i2o_len;
void *i2o_addr;
unsigned int drv_data;
unsigned int dmabits; /* 32 | 64 */
unsigned int registered;
unsigned int present;
unsigned int msi;
spinlock_t lock;
/* API Related */
unsigned int chd_dec_major;
unsigned int cfg_users;
crystalhd_ioctl_data *idata_free_head; /* ioctl data pool */
crystalhd_elem_t *elem_pool_head; /* Queue element pool */
struct crystalhd_cmd cmds;
crystalhd_dio_req *ua_map_free_head;
struct pci_pool *fill_byte_pool;
};
struct crystalhd_adp *chd_get_adp(void);
void chd_set_log_level(struct crystalhd_adp *adp, char *arg);
#endif
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_misc . c
*
* Description:
* BCM70012 Linux driver misc routines.
*
* HISTORY:
*
**********************************************************************
* This file is part of the crystalhd device driver.
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver. If not, see <http://www.gnu.org/licenses/>.
**********************************************************************/
#include "crystalhd_misc.h"
#include "crystalhd_lnx.h"
uint32_t g_linklog_level;
static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
{
crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
}
static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
{
crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
}
static inline BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
{
return BC_STS_SUCCESS;
}
static crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
{
unsigned long flags = 0;
crystalhd_dio_req *temp = NULL;
if (!adp) {
BCMLOG_ERR("Invalid Arg!!\n");
return temp;
}
spin_lock_irqsave(&adp->lock, flags);
temp = adp->ua_map_free_head;
if (temp)
adp->ua_map_free_head = adp->ua_map_free_head->next;
spin_unlock_irqrestore(&adp->lock, flags);
return temp;
}
static void crystalhd_free_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
{
unsigned long flags = 0;
if (!adp || !dio)
return;
spin_lock_irqsave(&adp->lock, flags);
dio->sig = crystalhd_dio_inv;
dio->page_cnt = 0;
dio->fb_size = 0;
memset(&dio->uinfo, 0, sizeof(dio->uinfo));
dio->next = adp->ua_map_free_head;
adp->ua_map_free_head = dio;
spin_unlock_irqrestore(&adp->lock, flags);
}
static crystalhd_elem_t *crystalhd_alloc_elem(struct crystalhd_adp *adp)
{
unsigned long flags = 0;
crystalhd_elem_t *temp = NULL;
if (!adp)
return temp;
spin_lock_irqsave(&adp->lock, flags);
temp = adp->elem_pool_head;
if (temp) {
adp->elem_pool_head = adp->elem_pool_head->flink;
memset(temp, 0, sizeof(*temp));
}
spin_unlock_irqrestore(&adp->lock, flags);
return temp;
}
static void crystalhd_free_elem(struct crystalhd_adp *adp, crystalhd_elem_t *elem)
{
unsigned long flags = 0;
if (!adp || !elem)
return;
spin_lock_irqsave(&adp->lock, flags);
elem->flink = adp->elem_pool_head;
adp->elem_pool_head = elem;
spin_unlock_irqrestore(&adp->lock, flags);
}
static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
unsigned int len, unsigned int offset)
{
sg_set_page(sg, page, len, offset);
#ifdef CONFIG_X86_64
sg->dma_length = len;
#endif
}
static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
{
/* http://lkml.org/lkml/2007/11/27/68 */
sg_init_table(sg, entries);
}
/*========================== Extern ========================================*/
/**
* bc_dec_reg_rd - Read 7412's device register.
* @adp: Adapter instance
* @reg_off: Register offset.
*
* Return:
* 32bit value read
*
* 7412's device register read routine. This interface use
* 7412's device access range mapped from BAR-2 (4M) of PCIe
* configuration space.
*/
uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
{
if (!adp || (reg_off > adp->pci_mem_len)) {
BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
return 0;
}
return readl(adp->addr + reg_off);
}
/**
* bc_dec_reg_wr - Write 7412's device register
* @adp: Adapter instance
* @reg_off: Register offset.
* @val: Dword value to be written.
*
* Return:
* none.
*
* 7412's device register write routine. This interface use
* 7412's device access range mapped from BAR-2 (4M) of PCIe
* configuration space.
*/
void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
{
if (!adp || (reg_off > adp->pci_mem_len)) {
BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
return;
}
writel(val, adp->addr + reg_off);
udelay(8);
}
/**
* crystalhd_reg_rd - Read Link's device register.
* @adp: Adapter instance
* @reg_off: Register offset.
*
* Return:
* 32bit value read
*
* Link device register read routine. This interface use
* Link's device access range mapped from BAR-1 (64K) of PCIe
* configuration space.
*
*/
uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
{
if (!adp || (reg_off > adp->pci_i2o_len)) {
BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
return 0;
}
return readl(adp->i2o_addr + reg_off);
}
/**
* crystalhd_reg_wr - Write Link's device register
* @adp: Adapter instance
* @reg_off: Register offset.
* @val: Dword value to be written.
*
* Return:
* none.
*
* Link device register write routine. This interface use
* Link's device access range mapped from BAR-1 (64K) of PCIe
* configuration space.
*
*/
void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
{
if (!adp || (reg_off > adp->pci_i2o_len)) {
BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
return;
}
writel(val, adp->i2o_addr + reg_off);
}
/**
* crystalhd_mem_rd - Read data from 7412's DRAM area.
* @adp: Adapter instance
* @start_off: Start offset.
* @dw_cnt: Count in dwords.
* @rd_buff: Buffer to copy the data from dram.
*
* Return:
* Status.
*
* 7412's Dram read routine.
*/
BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
uint32_t dw_cnt, uint32_t *rd_buff)
{
uint32_t ix = 0;
if (!adp || !rd_buff ||
(bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
for (ix = 0; ix < dw_cnt; ix++)
rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
return BC_STS_SUCCESS;
}
/**
* crystalhd_mem_wr - Write data to 7412's DRAM area.
* @adp: Adapter instance
* @start_off: Start offset.
* @dw_cnt: Count in dwords.
* @wr_buff: Data Buffer to be written.
*
* Return:
* Status.
*
* 7412's Dram write routine.
*/
BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
uint32_t dw_cnt, uint32_t *wr_buff)
{
uint32_t ix = 0;
if (!adp || !wr_buff ||
(bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
for (ix = 0; ix < dw_cnt; ix++)
crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
return BC_STS_SUCCESS;
}
/**
* crystalhd_pci_cfg_rd - PCIe config read
* @adp: Adapter instance
* @off: PCI config space offset.
* @len: Size -- Byte, Word & dword.
* @val: Value read
*
* Return:
* Status.
*
* Get value from Link's PCIe config space.
*/
BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
uint32_t len, uint32_t *val)
{
BC_STATUS sts = BC_STS_SUCCESS;
int rc = 0;
if (!adp || !val) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
switch (len) {
case 1:
rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
break;
case 2:
rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
break;
case 4:
rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
break;
default:
rc = -EINVAL;
sts = BC_STS_INV_ARG;
BCMLOG_ERR("Invalid len:%d\n", len);
};
if (rc && (sts == BC_STS_SUCCESS))
sts = BC_STS_ERROR;
return sts;
}
/**
* crystalhd_pci_cfg_wr - PCIe config write
* @adp: Adapter instance
* @off: PCI config space offset.
* @len: Size -- Byte, Word & dword.
* @val: Value to be written
*
* Return:
* Status.
*
* Set value to Link's PCIe config space.
*/
BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
uint32_t len, uint32_t val)
{
BC_STATUS sts = BC_STS_SUCCESS;
int rc = 0;
if (!adp || !val) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
switch (len) {
case 1:
rc = pci_write_config_byte(adp->pdev, off, (u8)val);
break;
case 2:
rc = pci_write_config_word(adp->pdev, off, (u16)val);
break;
case 4:
rc = pci_write_config_dword(adp->pdev, off, val);
break;
default:
rc = -EINVAL;
sts = BC_STS_INV_ARG;
BCMLOG_ERR("Invalid len:%d\n", len);
};
if (rc && (sts == BC_STS_SUCCESS))
sts = BC_STS_ERROR;
return sts;
}
/**
* bc_kern_dma_alloc - Allocate memory for Dma rings
* @adp: Adapter instance
* @sz: Size of the memory to allocate.
* @phy_addr: Physical address of the memory allocated.
* Typedef to system's dma_addr_t (u64)
*
* Return:
* Pointer to allocated memory..
*
* Wrapper to Linux kernel interface.
*
*/
void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
dma_addr_t *phy_addr)
{
void *temp = NULL;
if (!adp || !sz || !phy_addr) {
BCMLOG_ERR("Invalide Arg..\n");
return temp;
}
temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
if (temp)
memset(temp, 0, sz);
return temp;
}
/**
* bc_kern_dma_free - Release Dma ring memory.
* @adp: Adapter instance
* @sz: Size of the memory to allocate.
* @ka: Kernel virtual address returned during _dio_alloc()
* @phy_addr: Physical address of the memory allocated.
* Typedef to system's dma_addr_t (u64)
*
* Return:
* none.
*/
void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
dma_addr_t phy_addr)
{
if (!adp || !ka || !sz || !phy_addr) {
BCMLOG_ERR("Invalide Arg..\n");
return;
}
pci_free_consistent(adp->pdev, sz, ka, phy_addr);
}
/**
* crystalhd_create_dioq - Create Generic DIO queue
* @adp: Adapter instance
* @dioq_hnd: Handle to the dio queue created
* @cb : Optional - Call back To free the element.
* @cbctx: Context to pass to callback.
*
* Return:
* status
*
* Initialize Generic DIO queue to hold any data. Callback
* will be used to free elements while deleting the queue.
*/
BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
crystalhd_dioq_t **dioq_hnd,
crystalhd_data_free_cb cb, void *cbctx)
{
crystalhd_dioq_t *dioq = NULL;
if (!adp || !dioq_hnd) {
BCMLOG_ERR("Invalid arg!!\n");
return BC_STS_INV_ARG;
}
dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
if (!dioq)
return BC_STS_INSUFF_RES;
spin_lock_init(&dioq->lock);
dioq->sig = BC_LINK_DIOQ_SIG;
dioq->head = (crystalhd_elem_t *)&dioq->head;
dioq->tail = (crystalhd_elem_t *)&dioq->head;
crystalhd_create_event(&dioq->event);
dioq->adp = adp;
dioq->data_rel_cb = cb;
dioq->cb_context = cbctx;
*dioq_hnd = dioq;
return BC_STS_SUCCESS;
}
/**
* crystalhd_delete_dioq - Delete Generic DIO queue
* @adp: Adapter instance
* @dioq: DIOQ instance..
*
* Return:
* None.
*
* Release Generic DIO queue. This function will remove
* all the entries from the Queue and will release data
* by calling the call back provided during creation.
*
*/
void crystalhd_delete_dioq(struct crystalhd_adp *adp, crystalhd_dioq_t *dioq)
{
void *temp;
if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
return;
do {
temp = crystalhd_dioq_fetch(dioq);
if (temp && dioq->data_rel_cb)
dioq->data_rel_cb(dioq->cb_context, temp);
} while (temp);
dioq->sig = 0;
kfree(dioq);
}
/**
* crystalhd_dioq_add - Add new DIO request element.
* @ioq: DIO queue instance
* @t: DIO request to be added.
* @wake: True - Wake up suspended process.
* @tag: Special tag to assign - For search and get.
*
* Return:
* Status.
*
* Insert new element to Q tail.
*/
BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data,
bool wake, uint32_t tag)
{
unsigned long flags = 0;
crystalhd_elem_t *tmp;
if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
BCMLOG_ERR("Invalid arg!!\n");
return BC_STS_INV_ARG;
}
tmp = crystalhd_alloc_elem(ioq->adp);
if (!tmp) {
BCMLOG_ERR("No free elements.\n");
return BC_STS_INSUFF_RES;
}
tmp->data = data;
tmp->tag = tag;
spin_lock_irqsave(&ioq->lock, flags);
tmp->flink = (crystalhd_elem_t *)&ioq->head;
tmp->blink = ioq->tail;
tmp->flink->blink = tmp;
tmp->blink->flink = tmp;
ioq->count++;
spin_unlock_irqrestore(&ioq->lock, flags);
if (wake)
crystalhd_set_event(&ioq->event);
return BC_STS_SUCCESS;
}
/**
* crystalhd_dioq_fetch - Fetch element from head.
* @ioq: DIO queue instance
*
* Return:
* data element from the head..
*
* Remove an element from Queue.
*/
void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq)
{
unsigned long flags = 0;
crystalhd_elem_t *tmp;
crystalhd_elem_t *ret = NULL;
void *data = NULL;
if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
BCMLOG_ERR("Invalid arg!!\n");
return data;
}
spin_lock_irqsave(&ioq->lock, flags);
tmp = ioq->head;
if (tmp != (crystalhd_elem_t *)&ioq->head) {
ret = tmp;
tmp->flink->blink = tmp->blink;
tmp->blink->flink = tmp->flink;
ioq->count--;
}
spin_unlock_irqrestore(&ioq->lock, flags);
if (ret) {
data = ret->data;
crystalhd_free_elem(ioq->adp, ret);
}
return data;
}
/**
* crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
* @ioq: DIO queue instance
* @tag: Tag to search for.
*
* Return:
* element from the head..
*
* Search TAG and remove the element.
*/
void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag)
{
unsigned long flags = 0;
crystalhd_elem_t *tmp;
crystalhd_elem_t *ret = NULL;
void *data = NULL;
if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
BCMLOG_ERR("Invalid arg!!\n");
return data;
}
spin_lock_irqsave(&ioq->lock, flags);
tmp = ioq->head;
while (tmp != (crystalhd_elem_t *)&ioq->head) {
if (tmp->tag == tag) {
ret = tmp;
tmp->flink->blink = tmp->blink;
tmp->blink->flink = tmp->flink;
ioq->count--;
break;
}
tmp = tmp->flink;
}
spin_unlock_irqrestore(&ioq->lock, flags);
if (ret) {
data = ret->data;
crystalhd_free_elem(ioq->adp, ret);
}
return data;
}
/**
* crystalhd_dioq_fetch_wait - Fetch element from Head.
* @ioq: DIO queue instance
* @to_secs: Wait timeout in seconds..
*
* Return:
* element from the head..
*
* Return element from head if Q is not empty. Wait for new element
* if Q is empty for Timeout seconds.
*/
void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs,
uint32_t *sig_pend)
{
unsigned long flags = 0;
int rc = 0, count;
void *tmp = NULL;
if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
BCMLOG_ERR("Invalid arg!!\n");
return tmp;
}
count = to_secs;
spin_lock_irqsave(&ioq->lock, flags);
while ((ioq->count == 0) && count) {
spin_unlock_irqrestore(&ioq->lock, flags);
crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
if (rc == 0) {
goto out;
} else if (rc == -EINTR) {
BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
*sig_pend = 1;
return tmp;
}
spin_lock_irqsave(&ioq->lock, flags);
count--;
}
spin_unlock_irqrestore(&ioq->lock, flags);
out:
return crystalhd_dioq_fetch(ioq);
}
/**
* crystalhd_map_dio - Map user address for DMA
* @adp: Adapter instance
* @ubuff: User buffer to map.
* @ubuff_sz: User buffer size.
* @uv_offset: UV buffer offset.
* @en_422mode: TRUE:422 FALSE:420 Capture mode.
* @dir_tx: TRUE for Tx (To device from host)
* @dio_hnd: Handle to mapped DIO request.
*
* Return:
* Status.
*
* This routine maps user address and lock pages for DMA.
*
*/
BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
uint32_t ubuff_sz, uint32_t uv_offset,
bool en_422mode, bool dir_tx,
crystalhd_dio_req **dio_hnd)
{
crystalhd_dio_req *dio;
/* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
unsigned long start = 0, end = 0, uaddr = 0, count = 0;
unsigned long spsz = 0, uv_start = 0;
int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
/* Compute pages */
uaddr = (unsigned long)ubuff;
count = (unsigned long)ubuff_sz;
end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
start = uaddr >> PAGE_SHIFT;
nr_pages = end - start;
if (!count || ((uaddr + count) < uaddr)) {
BCMLOG_ERR("User addr overflow!!\n");
return BC_STS_INV_ARG;
}
dio = crystalhd_alloc_dio(adp);
if (!dio) {
BCMLOG_ERR("dio pool empty..\n");
return BC_STS_INSUFF_RES;
}
if (dir_tx) {
rw = WRITE;
dio->direction = DMA_TO_DEVICE;
} else {
rw = READ;
dio->direction = DMA_FROM_DEVICE;
}
if (nr_pages > dio->max_pages) {
BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
dio->max_pages, nr_pages);
crystalhd_unmap_dio(adp, dio);
return BC_STS_INSUFF_RES;
}
if (uv_offset) {
uv_start = (uaddr + (unsigned long)uv_offset) >> PAGE_SHIFT;
dio->uinfo.uv_sg_ix = uv_start - start;
dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
}
dio->fb_size = ubuff_sz & 0x03;
if (dio->fb_size) {
res = copy_from_user(dio->fb_va,
(void *)(uaddr + count - dio->fb_size),
dio->fb_size);
if (res) {
BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
res, dio->fb_size,
(void *)(uaddr + count-dio->fb_size));
crystalhd_unmap_dio(adp, dio);
return BC_STS_INSUFF_RES;
}
}
down_read(&current->mm->mmap_sem);
res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
0, dio->pages, NULL);
up_read(&current->mm->mmap_sem);
/* Save for release..*/
dio->sig = crystalhd_dio_locked;
if (res < nr_pages) {
BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
dio->page_cnt = res;
crystalhd_unmap_dio(adp, dio);
return BC_STS_ERROR;
}
dio->page_cnt = nr_pages;
/* Get scatter/gather */
crystalhd_init_sg(dio->sg, dio->page_cnt);
crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
if (nr_pages > 1) {
dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
#ifdef CONFIG_X86_64
dio->sg[0].dma_length = dio->sg[0].length;
#endif
count -= dio->sg[0].length;
for (i = 1; i < nr_pages; i++) {
if (count < 4) {
spsz = count;
skip_fb_sg = 1;
} else {
spsz = (count < PAGE_SIZE) ?
(count & ~0x03) : PAGE_SIZE;
}
crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
count -= spsz;
}
} else {
if (count < 4) {
dio->sg[0].length = count;
skip_fb_sg = 1;
} else {
dio->sg[0].length = count - dio->fb_size;
}
#ifdef CONFIG_X86_64
dio->sg[0].dma_length = dio->sg[0].length;
#endif
}
dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
dio->page_cnt, dio->direction);
if (dio->sg_cnt <= 0) {
BCMLOG_ERR("sg map %d-%d \n", dio->sg_cnt, dio->page_cnt);
crystalhd_unmap_dio(adp, dio);
return BC_STS_ERROR;
}
if (dio->sg_cnt && skip_fb_sg)
dio->sg_cnt -= 1;
dio->sig = crystalhd_dio_sg_mapped;
/* Fill in User info.. */
dio->uinfo.xfr_len = ubuff_sz;
dio->uinfo.xfr_buff = ubuff;
dio->uinfo.uv_offset = uv_offset;
dio->uinfo.b422mode = en_422mode;
dio->uinfo.dir_tx = dir_tx;
*dio_hnd = dio;
return BC_STS_SUCCESS;
}
/**
* crystalhd_unmap_sgl - Release mapped resources
* @adp: Adapter instance
* @dio: DIO request instance
*
* Return:
* Status.
*
* This routine is to unmap the user buffer pages.
*/
BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
{
struct page *page = NULL;
int j = 0;
if (!adp || !dio) {
BCMLOG_ERR("Invalid arg \n");
return BC_STS_INV_ARG;
}
if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
for (j = 0; j < dio->page_cnt; j++) {
page = dio->pages[j];
if (page) {
if (!PageReserved(page) &&
(dio->direction == DMA_FROM_DEVICE))
SetPageDirty(page);
page_cache_release(page);
}
}
}
if (dio->sig == crystalhd_dio_sg_mapped)
pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
crystalhd_free_dio(adp, dio);
return BC_STS_SUCCESS;
}
/**
* crystalhd_create_dio_pool - Allocate mem pool for DIO management.
* @adp: Adapter instance
* @max_pages: Max pages for size calculation.
*
* Return:
* system error.
*
* This routine creates a memory pool to hold dio context for
* for HW Direct IO operation.
*/
int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
{
uint32_t asz = 0, i = 0;
uint8_t *temp;
crystalhd_dio_req *dio;
if (!adp || !max_pages) {
BCMLOG_ERR("Invalid Arg!!\n");
return -EINVAL;
}
/* Get dma memory for fill byte handling..*/
adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
adp->pdev, 8, 8, 0);
if (!adp->fill_byte_pool) {
BCMLOG_ERR("failed to create fill byte pool\n");
return -ENOMEM;
}
/* Get the max size from user based on 420/422 modes */
asz = (sizeof(*dio->pages) * max_pages) +
(sizeof(*dio->sg) * max_pages) + sizeof(*dio);
BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
temp = (uint8_t *)kzalloc(asz, GFP_KERNEL);
if ((temp) == NULL) {
BCMLOG_ERR("Failed to alloc %d mem\n", asz);
return -ENOMEM;
}
dio = (crystalhd_dio_req *)temp;
temp += sizeof(*dio);
dio->pages = (struct page **)temp;
temp += (sizeof(*dio->pages) * max_pages);
dio->sg = (struct scatterlist *)temp;
dio->max_pages = max_pages;
dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
&dio->fb_pa);
if (!dio->fb_va) {
BCMLOG_ERR("fill byte alloc failed.\n");
return -ENOMEM;
}
crystalhd_free_dio(adp, dio);
}
return 0;
}
/**
* crystalhd_destroy_dio_pool - Release DIO mem pool.
* @adp: Adapter instance
*
* Return:
* none.
*
* This routine releases dio memory pool during close.
*/
void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
{
crystalhd_dio_req *dio;
int count = 0;
if (!adp) {
BCMLOG_ERR("Invalid Arg!!\n");
return;
}
do {
dio = crystalhd_alloc_dio(adp);
if (dio) {
if (dio->fb_va)
pci_pool_free(adp->fill_byte_pool,
dio->fb_va, dio->fb_pa);
count++;
kfree(dio);
}
} while (dio);
if (adp->fill_byte_pool) {
pci_pool_destroy(adp->fill_byte_pool);
adp->fill_byte_pool = NULL;
}
BCMLOG(BCMLOG_DBG, "Released dio pool %d \n", count);
}
/**
* crystalhd_create_elem_pool - List element pool creation.
* @adp: Adapter instance
* @pool_size: Number of elements in the pool.
*
* Return:
* 0 - success, <0 error
*
* Create general purpose list element pool to hold pending,
* and active requests.
*/
int 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;
}
}
}
/***************************************************************************
* Copyright (c) 2005-2009, Broadcom Corporation.
*
* Name: crystalhd_misc . h
*
* Description:
* BCM70012 Linux driver general purpose routines.
* Includes reg/mem read and write 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/>.
**********************************************************************/
#ifndef _CRYSTALHD_MISC_H_
#define _CRYSTALHD_MISC_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/ioctl.h>
#include <linux/dma-mapping.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <asm/system.h>
#include "bc_dts_glob_lnx.h"
/* Global log level variable defined in crystal_misc.c file */
extern uint32_t g_linklog_level;
/* Global element pool for all Queue management.
* TX: Active = BC_TX_LIST_CNT, Free = BC_TX_LIST_CNT.
* RX: Free = BC_RX_LIST_CNT, Active = 2
* FW-CMD: 4
*/
#define BC_LINK_ELEM_POOL_SZ ((BC_TX_LIST_CNT * 2) + BC_RX_LIST_CNT + 2 + 4)
/* Driver's IODATA pool count */
#define CHD_IODATA_POOL_SZ (BC_IOCTL_DATA_POOL_SIZE * BC_LINK_MAX_OPENS)
/* Scatter Gather memory pool size for Tx and Rx */
#define BC_LINK_SG_POOL_SZ (BC_TX_LIST_CNT + BC_RX_LIST_CNT)
enum _crystalhd_dio_sig {
crystalhd_dio_inv = 0,
crystalhd_dio_locked,
crystalhd_dio_sg_mapped,
};
struct crystalhd_dio_user_info {
void *xfr_buff;
uint32_t xfr_len;
uint32_t uv_offset;
bool dir_tx;
uint32_t uv_sg_ix;
uint32_t uv_sg_off;
int comp_sts;
int ev_sts;
uint32_t y_done_sz;
uint32_t uv_done_sz;
uint32_t comp_flags;
bool b422mode;
};
typedef struct _crystalhd_dio_req {
uint32_t sig;
uint32_t max_pages;
struct page **pages;
struct scatterlist *sg;
int sg_cnt;
int page_cnt;
int direction;
struct crystalhd_dio_user_info uinfo;
void *fb_va;
uint32_t fb_size;
dma_addr_t fb_pa;
struct _crystalhd_dio_req *next;
} crystalhd_dio_req;
#define BC_LINK_DIOQ_SIG (0x09223280)
typedef struct _crystalhd_elem_s {
struct _crystalhd_elem_s *flink;
struct _crystalhd_elem_s *blink;
void *data;
uint32_t tag;
} crystalhd_elem_t;
typedef void (*crystalhd_data_free_cb)(void *context, void *data);
typedef struct _crystalhd_dioq_s {
uint32_t sig;
struct crystalhd_adp *adp;
crystalhd_elem_t *head;
crystalhd_elem_t *tail;
uint32_t count;
spinlock_t lock;
wait_queue_head_t event;
crystalhd_data_free_cb data_rel_cb;
void *cb_context;
} crystalhd_dioq_t;
typedef void (*hw_comp_callback)(crystalhd_dio_req *,
wait_queue_head_t *event, BC_STATUS sts);
/*========= Decoder (7412) register access routines.================= */
uint32_t bc_dec_reg_rd(struct crystalhd_adp *, uint32_t);
void bc_dec_reg_wr(struct crystalhd_adp *, uint32_t, uint32_t);
/*========= Link (70012) register access routines.. =================*/
uint32_t crystalhd_reg_rd(struct crystalhd_adp *, uint32_t);
void crystalhd_reg_wr(struct crystalhd_adp *, uint32_t, uint32_t);
/*========= Decoder (7412) memory access routines..=================*/
BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
/*==========Link (70012) PCIe Config access routines.================*/
BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t);
/*========= Linux Kernel Interface routines. ======================= */
void *bc_kern_dma_alloc(struct crystalhd_adp *, uint32_t, dma_addr_t *);
void bc_kern_dma_free(struct crystalhd_adp *, uint32_t,
void *, dma_addr_t);
#define crystalhd_create_event(_ev) init_waitqueue_head(_ev)
#define crystalhd_set_event(_ev) wake_up_interruptible(_ev)
#define crystalhd_wait_on_event(ev, condition, timeout, ret, nosig) \
do { \
DECLARE_WAITQUEUE(entry, current); \
unsigned long end = jiffies + ((timeout * HZ) / 1000); \
ret = 0; \
add_wait_queue(ev, &entry); \
for (;;) { \
__set_current_state(TASK_INTERRUPTIBLE); \
if (condition) { \
break; \
} \
if (time_after_eq(jiffies, end)) { \
ret = -EBUSY; \
break; \
} \
schedule_timeout((HZ / 100 > 1) ? HZ / 100 : 1); \
if (!nosig && signal_pending(current)) { \
ret = -EINTR; \
break; \
} \
} \
__set_current_state(TASK_RUNNING); \
remove_wait_queue(ev, &entry); \
} while (0)
/*================ Direct IO mapping routines ==================*/
extern int crystalhd_create_dio_pool(struct crystalhd_adp *, uint32_t);
extern void crystalhd_destroy_dio_pool(struct crystalhd_adp *);
extern BC_STATUS crystalhd_map_dio(struct crystalhd_adp *, void *, uint32_t,
uint32_t, bool, bool, crystalhd_dio_req**);
extern BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *, crystalhd_dio_req*);
#define crystalhd_get_sgle_paddr(_dio, _ix) (cpu_to_le64(sg_dma_address(&_dio->sg[_ix])))
#define crystalhd_get_sgle_len(_dio, _ix) (cpu_to_le32(sg_dma_len(&_dio->sg[_ix])))
/*================ General Purpose Queues ==================*/
extern BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *, crystalhd_dioq_t **, crystalhd_data_free_cb , void *);
extern void crystalhd_delete_dioq(struct crystalhd_adp *, crystalhd_dioq_t *);
extern BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data, bool wake, uint32_t tag);
extern void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq);
extern void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag);
extern void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs, uint32_t *sig_pend);
#define crystalhd_dioq_count(_ioq) ((_ioq) ? _ioq->count : 0)
extern int crystalhd_create_elem_pool(struct crystalhd_adp *, uint32_t);
extern void crystalhd_delete_elem_pool(struct crystalhd_adp *);
/*================ Debug routines/macros .. ================================*/
extern void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount);
enum _chd_log_levels {
BCMLOG_ERROR = 0x80000000, /* Don't disable this option */
BCMLOG_DATA = 0x40000000, /* Data, enable by default */
BCMLOG_SPINLOCK = 0x20000000, /* Spcial case for Spin locks*/
/* Following are allowed only in debug mode */
BCMLOG_INFO = 0x00000001, /* Generic informational */
BCMLOG_DBG = 0x00000002, /* First level Debug info */
BCMLOG_SSTEP = 0x00000004, /* Stepping information */
BCMLOG_ENTER_LEAVE = 0x00000008, /* stack tracking */
};
#define BCMLOG_ENTER \
if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
printk("Entered %s\n", __func__); \
}
#define BCMLOG_LEAVE \
if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
printk("Leaving %s\n", __func__); \
}
#define BCMLOG(trace, fmt, args...) \
if (g_linklog_level & trace) { \
printk(fmt, ##args); \
}
#define BCMLOG_ERR(fmt, args...) \
do { \
if (g_linklog_level & BCMLOG_ERROR) { \
printk("*ERR*:%s:%d: "fmt, __FILE__, __LINE__, ##args); \
} \
} while (0);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment