mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-11 08:54:28 -05:00
12988 lines
385 KiB
Diff
12988 lines
385 KiB
Diff
From e20f20aaed6b6d2fd1667bad9be9ef35103a51df Mon Sep 17 00:00:00 2001
|
||
From: Liron Kuch <lkuch@codeaurora.org>
|
||
Date: Sun, 6 Sep 2015 11:19:39 +0300
|
||
Subject: msm: broadcast: Remove unused TSC and TSPP2 drivers
|
||
|
||
TSC and TSPP2 were HW blocks in MPQ8092 target which is
|
||
no longer supported. Remove TSC and TSPP2 drivers to
|
||
eliminate unused code.
|
||
|
||
Change-Id: Ibb55ae0d15b33ba5855bde69e78925d23def3c6b
|
||
Signed-off-by: Liron Kuch <lkuch@codeaurora.org>
|
||
---
|
||
Documentation/arm/msm/tsc.txt | 398 --
|
||
Documentation/arm/msm/tspp2.txt | 497 --
|
||
drivers/media/platform/msm/broadcast/Makefile | 2 -
|
||
drivers/media/platform/msm/broadcast/tsc.c | 3450 ----------
|
||
drivers/media/platform/msm/broadcast/tspp2.c | 8578 -------------------------
|
||
5 files changed, 12925 deletions(-)
|
||
delete mode 100644 Documentation/arm/msm/tsc.txt
|
||
delete mode 100644 Documentation/arm/msm/tspp2.txt
|
||
delete mode 100644 drivers/media/platform/msm/broadcast/tsc.c
|
||
delete mode 100644 drivers/media/platform/msm/broadcast/tspp2.c
|
||
|
||
diff --git a/Documentation/arm/msm/tsc.txt b/Documentation/arm/msm/tsc.txt
|
||
deleted file mode 100644
|
||
index 11e74a2..0000000
|
||
--- a/Documentation/arm/msm/tsc.txt
|
||
+++ /dev/null
|
||
@@ -1,398 +0,0 @@
|
||
-Introduction
|
||
-============
|
||
-
|
||
-TSC Driver
|
||
-
|
||
-The TSC (Transport Stream Controller) is a hardware block used in products such
|
||
-as smart TVs, Set-top boxes and digital media adapters, and is responsible for
|
||
-two main functionalities:
|
||
-
|
||
-1. Mux function: enabling the routing of MPEG-2 transport streams (TS) received
|
||
-from terrestrial/cable/satelite in order to support the different topologies of
|
||
-the end product, as it may be deployed in many different topologies.
|
||
-In addition, the active topology may change according to various factors such as
|
||
-broadcast technology and/or conditional access system.
|
||
-
|
||
-2. CI function: acting as a common interface, complying with both PC Card and
|
||
-CI/+ specifications.
|
||
-
|
||
-The TSC driver has two different interfaces, one for each function.
|
||
-
|
||
-Hardware description
|
||
-====================
|
||
-The TSC HW contains the TSC core, and uses the VBIF unit (IOMMU) which is part
|
||
-of the broadcast subsystem HW.
|
||
-
|
||
-Mux function:
|
||
--------------
|
||
-The TSC can receive transport streams from:
|
||
-a. Two Transport Stream Interfaces (TSIFs) 0 or 1, connected to two external
|
||
-demods or to external bridge.
|
||
-b. One TSIF from an integrated demod.
|
||
-
|
||
-The TSC can route TS from any of the above TSIFs to an external CICAM, using a
|
||
-software configurable mux.
|
||
-The TSC can route TS from any of the above TSIFs, and TS received from the CI
|
||
-Conditional Access Mudule (CICAM) to two TSIF outputs (0 or 1), using two
|
||
-software configurable muexes.
|
||
-The CICAM input and outputs are also managed via two additional TSIFs: TSIF-out
|
||
-to the CAM, and TSIF-in from the CAM.
|
||
-
|
||
-CI function:
|
||
-------------
|
||
-The common interface is composed of:
|
||
-1. Card detection logic: the TSC notifies the SW of any change in the card
|
||
-detection status (via HW interrupt).
|
||
-
|
||
-2. Control interface used to send/receive the CI messages (APDUs), supporting
|
||
-data transmission in two formats:
|
||
-a. Single byte transactions: to/from the attribute memory space of the CAM and
|
||
- the command area of the CAM.
|
||
-b. Buffer transactions: to/from the command area of the CAM, using a
|
||
- configurable buffer size of 1k bytes-64k bytes. This enables transferring
|
||
- large chunks of data between the CAM and applications.
|
||
- The data buffer resides in the external memory and the interface to the
|
||
- memory is done through BCSS VBIF.
|
||
-The TSC uses PCMCIA interface to interact with the CAM.
|
||
-
|
||
-The following diagram provides an overview of the TSC HW:
|
||
-+-------------------------------------------------------------------------+
|
||
-| |
|
||
-| +------------------------------+ |
|
||
-| +-----------+ | TSC Core --. | |
|
||
-| |Ext. TSIF 0+------------+------------>| \ | +-----------+ |
|
||
-| +-----------+ | +-----|------------>|Mux)----->TSPP TSIF 0| |
|
||
-| +-----------+ | | +--|------------>| / | +-----------+ |
|
||
-| |Ext. TSIF 1+------| | | +->--' | |
|
||
-| +-----------+ | | | | | --. | |
|
||
-| | | | +----------|->| \ | +-----------+ |
|
||
-| +-----------+ | +--|--|-+--------|->|Mux)----->TSPP TSIF 1| |
|
||
-| |Int. TSIF +---------+--|-|-+------|->| / | +-----------+ |
|
||
-| +-----------+ | | | | +->--' | |
|
||
-| | | | | | | |
|
||
-| | | | | | | |
|
||
-| |+------+(v-v-v--) | +-----+| |
|
||
-| ||Card | \ Mux / | |CI/+ +---Data-Interface--+ |
|
||
-| ||detect| `---' | +----++| | |
|
||
-| |+-^-^--+ | | | | | |
|
||
-| +--|-|-------|-------|-------|-+ +------+----+ |
|
||
-| | | | | | | VBIF | |
|
||
-| | | +-----v--+ +--+----+ | | | |
|
||
-| | | |TSIF-Out| |TSIF-In| | +-----------+ |
|
||
-| | | +-----+--+ +--^----+ | |
|
||
-| | | | | | |
|
||
-| ++-+-------v-------+-------++ |
|
||
-| | CICAM | |
|
||
-| | | |
|
||
-| +---------------------------+ |
|
||
-+-------------------------------------------------------------------------+
|
||
-
|
||
-Software description
|
||
-====================
|
||
-The TSC Linux kernel driver manages the TSC core. It is a standard Linux
|
||
-platform device driver. It can be configured as a loadable or built-in kernel
|
||
-module. The driver is supported only in platforms that contain the TSC HW.
|
||
-
|
||
-The TSC driver uses ION driver to control the IOMMU and map user-allocated
|
||
-buffers to the TSC IOMMU domain.
|
||
-
|
||
-The driver provides an abstraction of the TSC HW functionality for user-space
|
||
-clients via two separate interfaces: tsc_mux and tsc_ci. These interfaces may
|
||
-be used by upper layers to utilize the TSC HW for routing the TS and supporting
|
||
-the Common Interface specification.
|
||
-
|
||
-Driver initialization
|
||
----------------------
|
||
-The driver's probe function is invoked if there is a matching device tree node.
|
||
-The probe function gets the required memory resources (i.e., register address
|
||
-spaces) and maps them to kernel space for the driver's use.
|
||
-The probe function also requests the required IRQs, GPIOs and clocks, and gets
|
||
-the TSC IOMMU domain. The probe function also disables the TSIFs input.
|
||
-Finally, the function creates two character device drivers: "tsc_mux","tsc_ci".
|
||
-
|
||
-See API description in interface section.
|
||
-
|
||
-Data paths
|
||
------------
|
||
-The TSC does not process the TS data received from the TSIFs. It just manages
|
||
-the routing of that data.
|
||
-
|
||
-Control paths - Mux function
|
||
-----------------------------
|
||
-Example for routing the TS from external demod TSIF 0 to the CAM, and from the
|
||
-CAM to TSIF 1 of the TSPP:
|
||
-
|
||
-struct tsc_route tsif_cam = {TSC_SOURCE_EXTERNAL0, TSC_DEST_CICAM};
|
||
-struct tsc_route cam_tspp = {TSC_SOURCE_CICAM, TSC_DEST_TSPP1};
|
||
-int mux_fd, ret;
|
||
-enum tsc_source tsif0 = TSC_SOURCE_EXTERNAL0;
|
||
-enum tsc_source cam = TSC_SOURCE_CICAM;
|
||
-
|
||
-/* opening Mux char device */
|
||
-mux_fd = open("/dev/tsc_mux0");
|
||
-
|
||
-/* Configure the CAM mux to route TS from external demod TSIF 0: */
|
||
-ret = ioctl(mux_fd, TSC_CONFIG_ROUTE, &tsif_cam);
|
||
-
|
||
-/* Configure the TSPP TSIF 1 mux to route TS from CAM: */
|
||
-ret = ioctl(mux_fd, TSC_CONFIG_ROUTE, &cam_tspp);
|
||
-
|
||
-/* Enabling the external demod TSIF 0, and the CAM TSIF-in and TSIF-out */
|
||
-ret = ioctl(mux_fd, TSC_ENABLE_INPUT, &tsif0);
|
||
-ret = ioctl(mux_fd, TSC_ENABLE_INPUT, &cam);
|
||
-
|
||
-close(mux_fd);
|
||
-
|
||
-Control paths - CI function
|
||
----------------------------
|
||
-Example for writing a buffer to the CAM command area:
|
||
-
|
||
-Assumptions:
|
||
-1. The user allocated a buffer using ION driver and wrote to that buffer.
|
||
-Also, retrieved the ion fd of that buffer and saved it to:
|
||
-int buffer_fd;
|
||
-2. The user already performed buffer size negotiation with the CAM according to
|
||
-CI/+ specification, and had set the CAM size register with the buffer size. This
|
||
-size is saved to: int size;
|
||
-3. The user decided about the time the user wants to wait for the data
|
||
-transmission.
|
||
-struct tsc_buffer_mode buff_params = {buffer_fd, size, timeout};
|
||
-int ret;
|
||
-
|
||
-/* Perform a blocking write buffer transaction for at most timeout */
|
||
-ret = ioctl(fd, TSC_WRITE_CAM_BUFFER, &buff_params);
|
||
-/* ret indicate whether the transaction succeeded */
|
||
-
|
||
-Example for SW reset to the CAM (according to CI/+ specification):
|
||
-struct single_byte_mode cmd_params = {1, RS bit set, timeout};
|
||
-struct single_byte_mode stat_params = {1, not initialize, timeout};
|
||
-int ci_fd, ret;
|
||
-u8 data;
|
||
-
|
||
-/* opening CI char device */
|
||
-ci_fd = open("/dev/tsc_ci0");
|
||
-
|
||
-/* Setting the RS bit of the CAM command register */
|
||
-ret = ioctl(ci_fd, TSC_WRITE_CAM_IO, &cmd_params);
|
||
-
|
||
-/* Polling the FR bit of the CAM status register */
|
||
-ret = ioctl(ci_fd, TSC_READ_CAM_IO, &stat_params);
|
||
-data = stat_params.data;
|
||
-while (data & FR_BIT_MASK) {
|
||
- ret = ioctl(ci_fd, TSC_READ_CAM_IO, &stat_params);
|
||
- data = stat_params.data;
|
||
-}
|
||
-
|
||
-close(ci_fd);
|
||
-
|
||
-Design
|
||
-======
|
||
-The TSC driver is a regular Linux platform driver designed to support the
|
||
-TSC HW available on specific SoCs.
|
||
-
|
||
-The driver provides two user-space APIs: tsc_mux that allows the client full
|
||
-control over the configuration of the TS routing, and tsc_ci that enables the
|
||
-client to implement the Common Interface in front of the CAM. It does so while
|
||
-encapsulating HW implementation details that are not relevant to the clients.
|
||
-
|
||
-The driver enforces HW restrictions and checks for input parameters
|
||
-validity, providing a success or failure return value for each API function:
|
||
-0 upon success or negative value on failure. Errno parameter is set to indicate
|
||
-the failure reason.
|
||
-However, the driver does not enforce any high-level policy with regard to the
|
||
-correct use of the TSC HW for various use-cases.
|
||
-
|
||
-Power Management
|
||
-================
|
||
-The TSC driver prevents the CPU from sleeping while the HW is active by using
|
||
-wakeup_source API. When there are no open devices the driver releases the wakeup
|
||
-source. In a similar manner, the driver enables the HW clocks only when needed.
|
||
-
|
||
-SMP/multi-core
|
||
-==============
|
||
-The driver uses a spinlock to protect accesses to its internal databases,
|
||
-for synchronization between user control API and kernel interrupt handlers.
|
||
-
|
||
-The driver uses a mutex for all the Mux operations to synchronize access to the
|
||
-routing internal databases. The driver uses another mutex for all the CI
|
||
-operations to synchronize data sent and received to and from the CAM.
|
||
-
|
||
-Security
|
||
-========
|
||
-Although the TSC is the bridge the external conditional access module, it has no
|
||
-security aspects. Any protection which is needed is performed by the upper
|
||
-layers. For example, the messages which are written to the CAM are encrypted.
|
||
-Thus the TSC accesses only non-protected, HLOS accessible memory regions.
|
||
-
|
||
-Performance
|
||
-===========
|
||
-Control operations are not considered as performance critical.
|
||
-Most of the control operations are assumed to be fairly uncommon.
|
||
-
|
||
-Interface
|
||
-=========
|
||
-Kernel-space API
|
||
-----------------
|
||
-The TSC driver does not provide any kernel-space API, only a user-space API.
|
||
-
|
||
-User-space API
|
||
-----------------
|
||
-Open: upper layer can open tsc_mux device and/or tsc_ci device.
|
||
-Release: close the device and release all the allocated resources.
|
||
-Poll: two different functions- one for Mux, one for CI. The Mux poll wait for
|
||
-rate mismatch interrupt. The CI poll waits for card detection HW interrupt.
|
||
-The rate mismatch interrupt is not cleared in the interrupt handler because it
|
||
-will signal again all the time. Therefore it is cleared via a specific ioctl
|
||
-that upper layer can use after the problem is solved. Additionally, the
|
||
-interrupt is cleared when the card is removed.
|
||
-ioctl: two functions, one for mux and one for ci. The ioctl are specified below.
|
||
-
|
||
-TSC Mux - routing the TS:
|
||
--------------------------
|
||
-enum tsc_source {
|
||
- TSC_SOURCE_EXTERNAL0,
|
||
- TSC_SOURCE_EXTERNAL1,
|
||
- TSC_SOURCE_INTERNAL,
|
||
- TSC_SOURCE_CICAM
|
||
-};
|
||
-enum tsc_dest {
|
||
- TSC_DEST_TSPP0,
|
||
- TSC_DEST_TSPP1,
|
||
- TSC_DSET_CICAM
|
||
-};
|
||
-
|
||
-struct tsc_route {
|
||
- enum tsc_source source;
|
||
- enum tsc_dest dest;
|
||
-};
|
||
-
|
||
-#define TSC_CONFIG_ROUTE _IOW(TSC_IOCTL_BASE, 0, struct tsc_tspp_route)
|
||
-#define TSC_ENABLE_INPUT _IOW(TSC_IOCTL_BASE, 1, enum tsc_source)
|
||
-#define TSC_DISABLE_INPUT _IOW(TSC_IOCTL_BASE, 2, enum tsc_source)
|
||
-
|
||
-These 3 IOCTLs control the 3 muxes that route the TS, and enable/disable the
|
||
-TSIFs input.
|
||
-
|
||
-TSC Mux - configuring the TSIFs:
|
||
---------------------------------
|
||
-enum tsc_data_type {
|
||
- TSC_DATA_TYPE_SERIAL,
|
||
- TSC_DATA_TYPE_PARALLEL
|
||
-};
|
||
-enum tsc_receive_mode {
|
||
- TSC_RECEIVE_MODE_START_VALID,
|
||
- TSC_RECEIVE_MODE_START_ONLY,
|
||
- TSC_RECEIVE_MODE_VALID_ONLY
|
||
-};
|
||
-
|
||
-struct tsc_tsif_params {
|
||
- enum tsc_source source;
|
||
- enum tsc_receive_mode receive_mode;
|
||
- enum tsc_data_type data_type;
|
||
- int clock_polarity;
|
||
- int data_polarity;
|
||
- int start_polarity;
|
||
- int valid_polarity;
|
||
- int error_polarity;
|
||
- int data_swap;
|
||
- int set_error;
|
||
-};
|
||
-
|
||
-#define TSC_SET_TSIF_CONFIG _IOW(TSC_IOCTL_BASE, 3, struct tsc_tsif_params)
|
||
-
|
||
-This IOCTL enables configuring a specific TSIF with all possible configurations.
|
||
-
|
||
-TSC Mux - clearing rate mismatch interrupt
|
||
-------------------------------------------
|
||
-
|
||
-#define TSC_CLEAR_RATE_MISMATCH_IRQ _IO(TSC_IOCTL_BASE, 4)
|
||
-
|
||
-This IOCTL is used for clearing the interrupt, which is not done automatically
|
||
-by the driver.
|
||
-
|
||
-TSC CI - CAM configuration:
|
||
----------------------------
|
||
-enum tsc_cam_personality {
|
||
- TSC_CICAM_PERSONALITY_CI,
|
||
- TSC_CICAM_PERSONALITY_CIPLUS,
|
||
- TSC_CICAM_PERSONALITY_PCCARD,
|
||
- TSC_CICAM_PERSONALITY_DISABLE
|
||
-};
|
||
-enum tsc_card_status {
|
||
- TSC_CARD_STATUS_NOT_DETECTED,
|
||
- TSC_CARD_STATUS_DETECTED,
|
||
- TSC_CARD_STATUS_FAILURE
|
||
-};
|
||
-
|
||
-#define TSC_CICAM_SET_CLOCK _IOW(TSC_IOCTL_BASE, 5, int)
|
||
-This IOCTL sets the clock rate of the TS from the TSC to the CAM
|
||
-
|
||
-#define TSC_CAM_RESET _IO(TSC_IOCTL_BASE, 6)
|
||
-This IOCTL performs HW reset to the CAM
|
||
-
|
||
-#define TSC_CICAM_PERSONALITY_CHANGE \
|
||
- _IOW(TSC_IOCTL_BASE, 7, enum tsc_cam_personality)
|
||
-This IOCTL configures the PCMCIA pins according to the specified card type.
|
||
-
|
||
-#define TSC_GET_CARD_STATUS _IOR(TSC_IOCTL_BASE, 8, enum tsc_card_status)
|
||
-This IOCTL queries the card detection pins and returns their status.
|
||
-
|
||
-TSC CI - Data transactions:
|
||
----------------------------
|
||
-struct tsc_single_byte_mode {
|
||
- u16 address;
|
||
- u8 data;
|
||
- int timeout; /* in msec */
|
||
-};
|
||
-struct tsc_buffer_mode {
|
||
- int buffer_fd;
|
||
- u16 buffer_size;
|
||
- int timeout; /* in msec */
|
||
-};
|
||
-
|
||
-#define TSC_READ_CAM_MEMORY \
|
||
- _IOWR(TSC_IOCTL_BASE, 9, struct tsc_single_byte_mode)
|
||
-#define TSC_WRITE_CAM_MEMORY \
|
||
- _IOW(TSC_IOCTL_BASE, 10, struct tsc_single_byte_mode)
|
||
-#define TSC_READ_CAM_IO \
|
||
- _IOWR(TSC_IOCTL_BASE, 11, struct tsc_single_byte_mode)
|
||
-#define TSC_WRITE_CAM_IO \
|
||
- _IOW(TSC_IOCTL_BASE, 12, struct tsc_single_byte_mode)
|
||
-#define TSC_READ_CAM_BUFFER \
|
||
- _IOWR(TSC_IOCTL_BASE, 13, struct tsc_buffer_mode)
|
||
-#define TSC_WRITE_CAM_BUFFER \
|
||
- _IOW(TSC_IOCTL_BASE, 14, struct tsc_buffer_mode)
|
||
-
|
||
-These IOCTLs performs a read/write data transaction of the requested type.
|
||
-
|
||
-Driver parameters
|
||
-=================
|
||
-The TSC module receives one parameter:
|
||
-tsc_iommu_bypass - 0 for using the VBIF, 1 for not using it. Not using the VBIF
|
||
-is a debug configuration.
|
||
-
|
||
-Config options
|
||
-==============
|
||
-To enable the driver, set CONFIG_TSC to y (built-in) or m (kernel module)
|
||
-in the kernel configuration menu.
|
||
-
|
||
-Dependencies
|
||
-============
|
||
-The TSC driver uses the ION driver for IOMMU registration and buffer
|
||
-mapping to BCSS VBIF.
|
||
-
|
||
-User space utilities
|
||
-====================
|
||
-None.
|
||
-
|
||
-Other
|
||
-=====
|
||
-None.
|
||
-
|
||
-Known issues
|
||
-============
|
||
-None.
|
||
-
|
||
-To do
|
||
-=====
|
||
-None.
|
||
diff --git a/Documentation/arm/msm/tspp2.txt b/Documentation/arm/msm/tspp2.txt
|
||
deleted file mode 100644
|
||
index 006c688..0000000
|
||
--- a/Documentation/arm/msm/tspp2.txt
|
||
+++ /dev/null
|
||
@@ -1,497 +0,0 @@
|
||
-Introduction
|
||
-============
|
||
-
|
||
-TSPP2 Driver
|
||
-
|
||
-The TSPP2 (Transport Stream Packet Processor v2) is a hardware accelerator
|
||
-designed to process MPEG-2 Transport Stream (TS) data. It can be used to
|
||
-process broadcast TV services. The TSPP2 HW processes the TS packets, offloads
|
||
-the host CPU and supports the real-time processing requirements of such
|
||
-services.
|
||
-
|
||
-TS data can be received either from TSIF (Transport Stream Interface) input
|
||
-or from memory input, to support playing live broadcasts as well as
|
||
-playback from memory. Recording is also supported.
|
||
-
|
||
-TSPP2 is a significantly different HW unit than the TSPP unit described in
|
||
-Documentation/arm/msm/tspp.txt. The functionality is enhanced and the HW
|
||
-design is different.
|
||
-
|
||
-Hardware description
|
||
-====================
|
||
-The TSPP2 HW contains the TSPP2 core, a BAM (Bus Access Manager, used for DMA
|
||
-operations) unit, and a VBIF unit (IOMMU).
|
||
-
|
||
-The TSPP2 HW supports:
|
||
-a. Up to two TSIF inputs and up to eight memory inputs.
|
||
-b. Various TS packet sizes (188/192 bytes) and formats (timestamp location).
|
||
-c. PID filtering.
|
||
-d. Raw transmit operation for section filtering or recording.
|
||
-e. Full PES and separated PES transmit operation for audio and video playback.
|
||
-f. Decryption and re-encryption operations for secure transport streams.
|
||
-g. PCR extraction.
|
||
-h. Indexing - identifying patterns in video streams.
|
||
-
|
||
-The following diagram provides an overview of the TSPP2 HW:
|
||
-+------------------------------------------------------------------+
|
||
-| |
|
||
-| +-------------+ +--------------------+ |
|
||
-| | TSIF 0 +---> TSPP2 Core | |
|
||
-| +-------------+ | | |
|
||
-| | +---------------+ | |
|
||
-| +-------------+ | | | | |
|
||
-| | TSIF 1 +---> | Source 0 | | |
|
||
-| +-------------+ | | | | |
|
||
-| | | | | |
|
||
-| | | | | |
|
||
-| | | +------------+| | +--------------+ |
|
||
-| | | | Filter 0 +|---------> BAM pipe 3 | |
|
||
-| | | +------------+| | +--------------+ |
|
||
-| | | +------------+| | +--------------+ |
|
||
-| +-------------+ | | | Filter 1 +|---------> BAM pipe 4 | |
|
||
-| | BAM pipe 0 +---> | +------------+| | +--------------+ |
|
||
-| +-------------+ | | | | | |
|
||
-| +-------------+ | +---------------+ | +--------------+ |
|
||
-| | BAM pipe 1 +--->--------------------|----| | |
|
||
-| +-------------+ | | | VBIF | |
|
||
-| +-------------+ | | | IOMMU | |
|
||
-| | BAM pipe 2 +--->--------------------|----| | |
|
||
-| +-------------+ +--------------------+ +--------------+ |
|
||
-+------------------------------------------------------------------+
|
||
-
|
||
-A source is configured to have either a TSIF input (TSIF 0 or 1) or a
|
||
-memory input (a BAM pipe). One or more filters are attached to the source.
|
||
-Each filter has a 13-bit PID and mask values to perform the PID filtering.
|
||
-Additionally, one or more operations are added to each filter to achieve the
|
||
-required functionality. Each operation has specific parameters. The operation's
|
||
-output is usually placed in an output pipe.
|
||
-
|
||
-The TSPP HW uses its own virtual address space, mapping memory buffer addresses
|
||
-using the VBIF IOMMU.
|
||
-
|
||
-Software description
|
||
-====================
|
||
-The TSPP2 Linux kernel driver manages the TSPP2 core. The TSPP2 driver utilizes
|
||
-the SPS driver to configure and manage the BAM unit, which is used to perform
|
||
-DMA operations and move TS data to/from system memory.
|
||
-
|
||
-The TSPP2 driver uses the ION driver to control the IOMMU and map user-allocated
|
||
-buffers to the TSPP2 IOMMU domain.
|
||
-
|
||
-The TSPP2 is a standard Linux platform device driver. It can be configured as a
|
||
-loadable or built-in kernel module. The driver is supported only in platforms
|
||
-that contain the TSPP2 HW.
|
||
-
|
||
-The driver provides an abstraction of the TSPP2 HW functionality for
|
||
-kernel-space clients. For example, the dvb/demux kernel driver, which provides
|
||
-an API for upper layers to perform TS de-multiplexing (including PID filtering,
|
||
-recording, indexing etc.), uses the TSPP2 driver to utilize the TSPP2 HW and
|
||
-offload the CPU, instead of doing all the required processing in SW.
|
||
-
|
||
-For further information please refer to Documentation/dvb/qcom-mpq.txt.
|
||
-
|
||
-Terminology
|
||
------------
|
||
-This section describes some of the software "objects" implemented by the driver.
|
||
-
|
||
-a. TSPP2 device: an instance of the TSPP2 device representing the TSPP2 HW and
|
||
-its capabilities. The client identifies a device instance according to a
|
||
-device ID.
|
||
-
|
||
-b. Indexing table: A TSPP2 device contains 4 indexing tables. These tables are
|
||
-used to identify patterns in the video stream and report on them.
|
||
-The client identifies an indexing table according to a table ID.
|
||
-
|
||
-c. Pipe: a BAM pipe used for DMA operations. The TSPP2 HW has a BAM unit with
|
||
-31 pipes. A pipe contains a memory buffer and a corresponding descriptor ring,
|
||
-and is used as the output for TSPP2 data (e.g. PES payload, PES headers,
|
||
-indexing information etc.). For memory inputs, a pipe is used as the input
|
||
-buffer where data can be written to for TSPP2 processing. BAM Pipes are
|
||
-managed by the TSPP2 driver using the SPS driver which controls BAM HW. The
|
||
-client is responsible for buffer memory allocation, and can control many
|
||
-BAM-related pipe parameters.
|
||
-
|
||
-d. Source: a source object represents data "stream" from the TS input,
|
||
-through the filters and operations that perform the processing on the TS data,
|
||
-until the output. A source has the following properties:
|
||
- - Either a TSIF or a memory input.
|
||
- - For memory input: an input pipe.
|
||
- - Source-related configuration (e.g., packet size and format).
|
||
- - One or more PID filters. Each filter contains operations.
|
||
- - One or more output pipes.
|
||
-The client is responsible to configure the source object as needed using the
|
||
-appropriate API. The client identifies a source using a source handle, which
|
||
-the driver provides when opening a source for use.
|
||
-
|
||
-e. Filter: a filter object represents a PID filter which is used to get only the
|
||
-TS packets with specific PIDs and filter out all other TS packets in the stream.
|
||
-The client adds filters to the source object to define the processing of data.
|
||
-Each filter has a 13-bit PID value and bit-mask, so a filter can be used to
|
||
-get TS packets with various PID values. Note, however, that it is highly
|
||
-recommended to use each filter with a unique PID (i.e., 0x1FFF mask), and it is
|
||
-mandatory that the PIDs handled by each source's filters are mutually exclusive
|
||
-(i.e., the client must not configure two filters in the same source that handle
|
||
-the same PID values). A filter has up to 16 operations that instruct the TSPP2
|
||
-HW how to process the data. The client identifies a filter using a filter
|
||
-handle, which the driver provides when opening a filter for use.
|
||
-
|
||
-f. Operation: an operation object represents a basic building block describing
|
||
-how data is processed. Operations are added to a filter and are performed on
|
||
-the data received by this filter, in the order they were added. One or more
|
||
-operations may be required to achieve the desired functionality. For example,
|
||
-a "section filtering" functionality requires a raw transmit operation, while a
|
||
-"recording" functionality requires a raw transmit operations as well as an
|
||
-indexing operation (to support trick modes).
|
||
-
|
||
-Driver initialization
|
||
----------------------
|
||
-The driver's probe function is invoked if there is a matching device tree node
|
||
-(or platform device). The probe function gets the required memory resources
|
||
-(i.e., register address spaces) and maps them to kernel space for the
|
||
-driver's use. The probe function also request the required IRQs and gets the
|
||
-TSPP2 IOMMU domain. Finally, the probe function resets all HW registers to
|
||
-appropriate default values, and resets all the required software structures.
|
||
-
|
||
-See API description in Interface section.
|
||
-
|
||
-Usage examples
|
||
---------------
|
||
-
|
||
-Section filtering example - opening a Raw filter with data from TSIF0:
|
||
-----------------------------------------------------------------------
|
||
-u32 dev_id = 0;
|
||
-u32 src_handle;
|
||
-u32 pipe_handle;
|
||
-u32 filter_handle;
|
||
-u32 iova;
|
||
-u32 vaddress;
|
||
-struct tspp2_config cfg = {...};
|
||
-struct tspp2_pipe_config_params pipe_config;
|
||
-struct tspp2_pipe_pull_mode_params pull_params = {0, 0};
|
||
-struct tspp2_operation raw_op;
|
||
-struct sps_event_notify event;
|
||
-struct sps_iovec desc;
|
||
-
|
||
-/* Open TSPP2 device for use */
|
||
-tspp2_device_open(dev_id);
|
||
-
|
||
-/* Set global configuration */
|
||
-tspp2_config_set(dev_id, &cfg);
|
||
-
|
||
-/* Open source with TSIF0 input */
|
||
-tspp2_src_open(dev_id, TSPP2_INPUT_TSIF0, &src_handle);
|
||
-
|
||
-/* Set parsing options if needed, for example: */
|
||
-tspp2_src_parsing_option_set(src_handle,
|
||
- TSPP2_SRC_PARSING_OPT_CHECK_CONTINUITY, 1);
|
||
-
|
||
-/* Assume normal sync byte, assume no need for scrambling configuration */
|
||
-
|
||
-/* Set packet size and format: */
|
||
-tspp2_src_packet_format_set(src_handle, TSPP2_PACKET_FORMAT_188_RAW);
|
||
-
|
||
-/* Since this is TSIF input, flow control is in push mode */
|
||
-
|
||
-/* Allocate memory for output pipe via ION – not shown here */
|
||
-
|
||
-/* Open an output pipe for use */
|
||
-pipe_config.ion_client = ...
|
||
-pipe_config.buffer_handle = ...
|
||
-pipe_config.buffer_size = ...
|
||
-pipe_config.pipe_mode = TSPP2_SRC_PIPE_OUTPUT;
|
||
-pipe_config.sps_cfg.descriptor_size = 188;
|
||
-pipe_config.sps_cfg.setting = (SPS_O_AUTO_ENABLE | SPS_O_HYBRID |
|
||
- SPS_O_OUT_OF_DESC | SPS_O_ACK_TRANSFERS);
|
||
-pipe_config.sps_cfg.wakeup_events = SPS_O_OUT_OF_DESC;
|
||
-pipe_config.sps_cfg.callback = ...
|
||
-pipe_config.sps_cfg.user_info = ...
|
||
-tspp2_pipe_open(dev_id, &pipe_config, &iova, &pipe_handle);
|
||
-
|
||
-/* Attache the pipe to the source */
|
||
-tspp2_src_pipe_attach(src_handle, pipe_handle, &pull_params);
|
||
-/* Open a filter for PID 13 */
|
||
-tspp2_filter_open(src_handle, 13, 0x1FFF, &filter_handle);
|
||
-
|
||
-/* Add a raw transmit operation */
|
||
-raw_op.type = TSPP2_OP_RAW_TRANSMIT;
|
||
-raw_op.params.raw_transmit.input = TSPP2_OP_BUFFER_A;
|
||
-raw_op.params.raw_transmit.timestamp_mode = TSPP2_OP_TIMESTAMP_NONE;
|
||
-raw_op.params.raw_transmit.skip_ts_packets_with_errors = 0;
|
||
-raw_op.params.raw_transmit.output_pipe_handle = pipe_handle;
|
||
-tspp2_filter_operations_add(filter_handle, &raw_op, 1);
|
||
-
|
||
-/* Enable filter and source to start getting data */
|
||
-tspp2_filter_enable(filter_handle);
|
||
-tspp2_source_enable(src_handle);
|
||
-
|
||
-/*
|
||
- * Data path: poll pipe (or get notifications from pipe via
|
||
- * registered callback).
|
||
- */
|
||
-tspp2_pipe_last_address_used_get(pipe_handle, &vaddress);
|
||
-
|
||
-/* Process data... */
|
||
-
|
||
-/* Get and release descriptors: */
|
||
-tspp2_pipe_descriptor_get(pipe_handle, &desc);
|
||
-tspp2_pipe_descriptor_put(pipe_handle, desc.addr, desc.size, ...);
|
||
-
|
||
-/* Teardown: */
|
||
-tspp2_src_disable(src_handle);
|
||
-tspp2_filter_disable(filter_handle);
|
||
-tspp2_filter_close(filter_handle);
|
||
-tspp2_src_pipe_detach(src_handle, pipe_handle);
|
||
-tspp2_pipe_close(pipe_handle);
|
||
-tspp2_src_close(src_handle);
|
||
-tspp2_device_close(dev_id);
|
||
-
|
||
-Debug facilities
|
||
-----------------
|
||
-The TSPP2 driver supports several debug facilities via debugfs:
|
||
-a. Ability to read the status of TSIF and TSPP2 HW registers via debugfs.
|
||
-b. Ability to print HW statistics, error and performance counters via debugfs.
|
||
-c. Ability to print SW status via debugfs.
|
||
-
|
||
-Design
|
||
-======
|
||
-The TSPP2 driver is a regular Linux platform driver designed to support the
|
||
-TSPP2 HW available on specific Qualcomm SoCs.
|
||
-
|
||
-The driver provides an extensive kernel-space API to allow the client full
|
||
-control over the configuration of the TSPP2 HW, while encapsulating HW
|
||
-implementation details that are not relevant to the client.
|
||
-
|
||
-The driver enforces HW restrictions and checks for input parameters
|
||
-validity, providing a success or failure return value for each API function.
|
||
-However, the driver does not enforce any high-level policy with regard to the
|
||
-correct use of the TSPP2 HW for various use-cases.
|
||
-
|
||
-Power Management
|
||
-================
|
||
-The TSPP2 driver prevents the CPU from sleeping while the HW is active by
|
||
-using the wakeup_source API. When the HW is not active (i.e., no sources
|
||
-configured), the driver indicates it is ready for system suspend by invoking
|
||
-__pm_relax(). When the HW needs to be active (i.e., a source has been opened and
|
||
-enabled), the driver invokes __pm_stay_awake().
|
||
-
|
||
-In a similar manner, the driver enables the HW clocks only when needed.
|
||
-The TSPP2 HW manages power saving automatically when the HW is not used.
|
||
-No SW involvement is required.
|
||
-
|
||
-SMP/multi-core
|
||
-==============
|
||
-The driver uses a mutex for mutual exclusion between kernel API calls.
|
||
-A spinlock is used to protect accesses to its internal databases which can be
|
||
-performed both from interrupt handler context and from API context.
|
||
-
|
||
-Security
|
||
-========
|
||
-None.
|
||
-
|
||
-Performance
|
||
-===========
|
||
-Control operations are not considered as performance critical.
|
||
-Most of the control operations are assumed to be fairly uncommon.
|
||
-Data-path operations involve only getting descriptors from the pipe and
|
||
-releasing them back to the pipe for reuse.
|
||
-
|
||
-Interface
|
||
-=========
|
||
-Kernel-space API
|
||
-----------------
|
||
-
|
||
-Control path API
|
||
--------------------
|
||
-
|
||
-TSPP2 device open / close API:
|
||
-------------------------------
|
||
-int tspp2_device_open(u32 dev_id);
|
||
-
|
||
-int tspp2_device_close(u32 dev_id);
|
||
-
|
||
-Global configuration for the TSPP2 device:
|
||
-------------------------------------------
|
||
-int tspp2_config_set(u32 dev_id, const struct tspp2_config *cfg);
|
||
- Set device global configuration.
|
||
-
|
||
-int tspp2_config_get(u32 dev_id, struct tspp2_config *cfg);
|
||
- Get current device global configuration.
|
||
-
|
||
-Configure Indexing Tables:
|
||
---------------------------
|
||
-int tspp2_indexing_prefix_set(u32 dev_id, u8 table_id, u32 value, u32 mask);
|
||
- Set prefix value and mask of an indexing table.
|
||
-
|
||
-int tspp2_indexing_patterns_add(u32 dev_id, u8 table_id, const u32 *values,
|
||
- const u32 *masks, u8 patterns_num);
|
||
- Add patterns to an indexing table.
|
||
-
|
||
-int tspp2_indexing_patterns_clear(u32 dev_id, u8 table_id);
|
||
- Clear all patterns of an indexing table
|
||
-
|
||
-Opening and closing Pipes:
|
||
---------------------------
|
||
-int tspp2_pipe_open(u32 dev_id, const struct tspp2_pipe_config_params *cfg,
|
||
- u32 *iova, u32 *pipe_handle);
|
||
- Open a pipe for use.
|
||
-
|
||
-int tspp2_pipe_close(u32 pipe_handle);
|
||
- Close an opened pipe.
|
||
-
|
||
-Source configuration:
|
||
----------------------
|
||
-int tspp2_src_open(u32 dev_id, enum tspp2_src_input input, u32 *src_handle);
|
||
- Open a new source for use.
|
||
-
|
||
-int tspp2_src_close(u32 src_handle);
|
||
- Close an opened source.
|
||
-
|
||
-int tspp2_src_parsing_option_set(u32 src_handle,
|
||
- enum tspp2_src_parsing_option option, int value);
|
||
- Set source parsing configuration option.
|
||
-
|
||
-int tspp2_src_parsing_option_get(u32 src_handle,
|
||
- enum tspp2_src_parsing_option option, int *value);
|
||
- Get source parsing configuration option.
|
||
-
|
||
-int tspp2_src_sync_byte_config_set(u32 src_handle, int check_sync_byte,
|
||
- u8 sync_byte_value);
|
||
- Set source sync byte configuration.
|
||
-
|
||
-int tspp2_src_sync_byte_config_get(u32 src_handle, int *check_sync_byte,
|
||
- u8 *sync_byte_value);
|
||
- Get source sync byte configuration.
|
||
-
|
||
-int tspp2_src_scrambling_config_set(u32 src_handle,
|
||
- const struct tspp2_src_scrambling_config *cfg);
|
||
- Set source scrambling configuration.
|
||
-
|
||
-int tspp2_src_scrambling_config_get(u32 src_handle,
|
||
- struct tspp2_src_scrambling_config *cfg);
|
||
- Get source scrambling configuration.
|
||
-
|
||
-int tspp2_src_packet_format_set(u32 src_handle,
|
||
- enum tspp2_packet_format format);
|
||
- Set source packet size and format.
|
||
-
|
||
-int tspp2_src_pipe_attach(u32 src_handle, u32 pipe_handle,
|
||
- const struct tspp2_pipe_pull_mode_params *cfg);
|
||
- Attach a pipe to a source.
|
||
-
|
||
-int tspp2_src_pipe_detach(u32 src_handle, u32 pipe_handle);
|
||
- Detach a pipe from a source.
|
||
-
|
||
-int tspp2_src_enable(u32 src_handle);
|
||
- Enable source (start using it).
|
||
-
|
||
-int tspp2_src_disable(u32 src_handle);
|
||
- Disable source (stop using it).
|
||
-
|
||
-int tspp2_src_filters_clear(u32 src_handle);
|
||
- Clear all filters from a source.
|
||
-
|
||
-Filter and Operation configuration:
|
||
------------------------------------
|
||
-int tspp2_filter_open(u32 src_handle, u16 pid, u16 mask, u32 *filter_handle);
|
||
- Open a new filter and add it to a source.
|
||
-
|
||
-int tspp2_filter_close(u32 filter_handle);
|
||
- Close a filter.
|
||
-
|
||
-int tspp2_filter_enable(u32 filter_handle);
|
||
- Enable a filter.
|
||
-
|
||
-int tspp2_filter_disable(u32 filter_handle);
|
||
- Disable a filter.
|
||
-
|
||
-int tspp2_filter_operations_set(u32 filter_handle,
|
||
- const struct tspp2_operation *ops, u8 operations_num);
|
||
- Set (add or update) operations to a filter.
|
||
-
|
||
-int tspp2_filter_operations_clear(u32 filter_handle);
|
||
- Clear all operations from a filter.
|
||
-
|
||
-int tspp2_filter_current_scrambling_bits_get(u32 filter_handle,
|
||
- u8 *scrambling_bits_value);
|
||
- Get the current scrambling bits.
|
||
-
|
||
-Events notifications registration:
|
||
-----------------------------------
|
||
-int tspp2_global_event_notification_register(u32 dev_id,
|
||
- u32 global_event_bitmask,
|
||
- void (*callback)(void *cookie),
|
||
- void *cookie);
|
||
- Get notified on a global event.
|
||
-
|
||
-int tspp2_src_event_notification_register(u32 src_handle,
|
||
- u32 src_event_bitmask,
|
||
- void (*callback)(void *cookie),
|
||
- void *cookie);
|
||
- Get notified on a source event.
|
||
-
|
||
-int tspp2_filter_event_notification_register(u32 filter_handle,
|
||
- u32 filter_event_bitmask,
|
||
- void (*callback)(void *cookie),
|
||
- void *cookie);
|
||
- Get notified on a filter event.
|
||
-
|
||
-Data path API
|
||
-----------------
|
||
-int tspp2_pipe_descriptor_get(u32 pipe_handle, struct sps_iovec *desc);
|
||
- Get a data descriptor from a pipe.
|
||
-
|
||
-int tspp2_pipe_descriptor_put(u32 pipe_handle, u32 addr,
|
||
- u32 size, u32 flags);
|
||
- Put (release) a descriptor for reuse by the pipe.
|
||
-
|
||
-int tspp2_pipe_last_address_used_get(u32 pipe_handle, u32 *address);
|
||
- Get the last address the TSPP2 used.
|
||
-
|
||
-int tspp2_data_write(u32 src_handle, u32 offset, u32 size);
|
||
- Write (feed) data to a source.
|
||
-
|
||
-User-space API
|
||
---------------
|
||
-The TSPP2 driver does not provide any user-space API, only a kernel-space API.
|
||
-The dvb/demux driver, which utilizes the TSPP2 driver (and HW), provides an
|
||
-extensive user-space API, allowing upper layers to achieve complex demuxing
|
||
-functionality.
|
||
-
|
||
-For further information please refer to Documentation/dvb/qcom-mpq.txt.
|
||
-
|
||
-Driver parameters
|
||
-=================
|
||
-The TSPP2 driver supports the following module parameter:
|
||
-tspp2_iommu_bypass: Bypass VBIF/IOMMU and use physical buffer addresses
|
||
-instead. This is mostly useful for debug purposes if something is wrong with
|
||
-the IOMMU configuration. Default is false.
|
||
-
|
||
-Platform-dependent parameters (e.g., IRQ numbers) are provided to the driver
|
||
-via the device tree mechanism or the platform device data mechanism.
|
||
-
|
||
-Config options
|
||
-==============
|
||
-To enable the driver, set CONFIG_TSPP2 to y (built-in) or m (kernel module)
|
||
-in the kernel configuration menu.
|
||
-
|
||
-Dependencies
|
||
-============
|
||
-a. The TSPP2 driver uses the SPS driver to control the BAM unit.
|
||
-b. The TSPP2 driver uses the ION driver for IOMMU registration and buffer
|
||
-mapping. The client is responsible to allocate memory buffers via ION.
|
||
-
|
||
-User space utilities
|
||
-====================
|
||
-None.
|
||
-
|
||
-Other
|
||
-=====
|
||
-None.
|
||
-
|
||
-Known issues
|
||
-============
|
||
-None.
|
||
-
|
||
-To do
|
||
-=====
|
||
-None.
|
||
diff --git a/drivers/media/platform/msm/broadcast/Makefile b/drivers/media/platform/msm/broadcast/Makefile
|
||
index 1233d6d..5e72b0d 100644
|
||
--- a/drivers/media/platform/msm/broadcast/Makefile
|
||
+++ b/drivers/media/platform/msm/broadcast/Makefile
|
||
@@ -3,9 +3,7 @@
|
||
#
|
||
|
||
obj-$(CONFIG_TSPP) += tspp.o
|
||
-obj-$(CONFIG_TSPP2) += tspp2.o
|
||
obj-$(CONFIG_CI_BRIDGE_SPI) += ci-bridge-spi.o
|
||
-obj-$(CONFIG_TSC) += tsc.o
|
||
obj-$(CONFIG_ENSIGMA_UCCP_330) += ensigma_uccp330.o
|
||
obj-$(CONFIG_DEMOD_WRAPPER) += demod_wrapper.o
|
||
|
||
diff --git a/drivers/media/platform/msm/broadcast/tsc.c b/drivers/media/platform/msm/broadcast/tsc.c
|
||
deleted file mode 100644
|
||
index ec3142e..0000000
|
||
--- a/drivers/media/platform/msm/broadcast/tsc.c
|
||
+++ /dev/null
|
||
@@ -1,3450 +0,0 @@
|
||
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||
- *
|
||
- * This program is free software; you can redistribute it and/or modify
|
||
- * it under the terms of the GNU General Public License version 2 and
|
||
- * only version 2 as published by the Free Software Foundation.
|
||
- *
|
||
- * This program 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.
|
||
- */
|
||
-
|
||
-#include <linux/tsc.h>
|
||
-#include <linux/init.h>
|
||
-#include <linux/module.h>
|
||
-#include <linux/device.h> /* Device drivers need this */
|
||
-#include <linux/cdev.h> /* Char device drivers need that */
|
||
-#include <linux/kernel.h> /* for KERN_INFO */
|
||
-#include <linux/fs.h>
|
||
-#include <linux/completion.h> /* for completion signaling after interrupts */
|
||
-#include <linux/uaccess.h> /* for copy from/to user in the ioctls */
|
||
-#include <linux/msm_iommu_domains.h>
|
||
-#include <linux/mutex.h>
|
||
-#include <linux/of.h> /* parsing device tree data */
|
||
-#include <linux/of_gpio.h>
|
||
-#include <linux/of_irq.h>
|
||
-#include <mach/gpio.h> /* gpios definitions */
|
||
-#include <linux/pinctrl/consumer.h> /* pinctrl API */
|
||
-#include <linux/clk.h>
|
||
-#include <linux/wait.h> /* wait() macros, sleeping */
|
||
-#include <linux/sched.h> /* Externally defined globals */
|
||
-#include <linux/poll.h> /* poll() file op */
|
||
-#include <linux/io.h> /* IO macros */
|
||
-#include <linux/bitops.h>
|
||
-#include <linux/msm_ion.h> /* ion_map_iommu */
|
||
-#include <linux/iommu.h>
|
||
-#include <linux/platform_device.h>
|
||
-#include <linux/slab.h> /* kfree, kzalloc */
|
||
-#include <linux/debugfs.h> /* debugfs support */
|
||
-#include <linux/pm_runtime.h> /* debugfs support */
|
||
-#include <linux/pm_wakeup.h> /* debugfs support */
|
||
-#include <linux/regulator/consumer.h> /* gdsc */
|
||
-#include <linux/msm-bus.h> /* bus client */
|
||
-#include <linux/delay.h> /* usleep function */
|
||
-/* TODO: include <linux/mpq_standby_if.h> after MCU is mainlined */
|
||
-
|
||
-/*
|
||
- * General defines
|
||
- */
|
||
-#define TEST_BIT(pos, number) (number & (1 << pos))
|
||
-#define CLEAR_BIT(pos, number) (number &= ~(1 << pos))
|
||
-#define SET_BIT(pos, number) (number |= 1 << pos)
|
||
-
|
||
-/*
|
||
- * extract bits [@b0:@b1] (inclusive) from the value @x
|
||
- * it should be @b0 <= @b1, or result is incorrect
|
||
- */
|
||
-static inline u32 GETL_BITS(u32 x, int b0, int b1)
|
||
-{
|
||
- return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
|
||
-}
|
||
-
|
||
-/* Bypass VBIF/IOMMU for debug and bring-up purposes */
|
||
-static int tsc_iommu_bypass; /* defualt=0 using iommu */
|
||
-module_param(tsc_iommu_bypass, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||
-
|
||
-/* The rate of the clock that control TS from TSC to the CAM */
|
||
-#define CICAM_CLK_RATE_12MHZ 12000000
|
||
-#define CICAM_CLK_RATE_9MHZ 8971962
|
||
-#define CICAM_CLK_RATE_7MHZ 7218045
|
||
-/* Rates for TSC serial and parallel clocks */
|
||
-#define TSC_SER_CLK_RATE 192000000
|
||
-#define TSC_PAR_CLK_RATE 24000000
|
||
-
|
||
-/* CICAM address space according to CI specification */
|
||
-#define CICAM_MAX_ADDRESS 3
|
||
-
|
||
-/*
|
||
- * TSC register offsets
|
||
- */
|
||
-#define TSC_HW_VERSION (0x0)
|
||
-#define TSC_MUX_CFG (0x4) /* Muxs config */
|
||
-#define TSC_IN_IFC_EXT (0x8) /* External demods tsifs */
|
||
-#define TSC_IN_IFC_CFG_INT (0xc) /* internal demods and
|
||
- cicam tsif config */
|
||
-#define TSC_FSM_STATE (0x50) /* Read FSM state */
|
||
-#define TSC_FSM_STATE_MASK (0x54) /* Config FSM state */
|
||
-#define TSC_CAM_CMD (0x1000)/* Config cam commands */
|
||
-#define TSC_CAM_RD_DATA (0x1004)/* read data for single-mode
|
||
- byte */
|
||
-#define TSC_STAT (0x1008)/* Interrupts status */
|
||
-#define TSC_IRQ_ENA (0x100C)/* Enable interrupts */
|
||
-#define TSC_IRQ_CLR (0x1010)/* Clear interrupts */
|
||
-#define TSC_CIP_CFG (0x1014)/* Enable HW polling */
|
||
-#define TSC_CD_STAT (0x1020)/* Card pins status */
|
||
-#define TSC_RD_BUFF_ADDR (0x1024)/* Vbif address for read
|
||
- buffer */
|
||
-#define TSC_WR_BUFF_ADDR (0x1028)/* Vbif address for write
|
||
- buffer */
|
||
-#define TSC_FALSE_CD (0x102C)/* Counter of false card
|
||
- detection */
|
||
-#define TSC_FALSE_CD_CLR (0x1030)/* Clear false cd counter */
|
||
-#define TSC_RESP_ERR (0x1034)/* State of read/write buffer
|
||
- error */
|
||
-#define TSC_CICAM_TSIF (0x1038)/* Enable tsif (tsc->cam) */
|
||
-
|
||
-
|
||
-/*
|
||
- * Registers structure definitions
|
||
- */
|
||
-
|
||
-/* TSC_MUX_CFG */
|
||
-#define MUX_EXTERNAL_DEMOD_0 0
|
||
-#define MUX_EXTERNAL_DEMOD_1 1
|
||
-#define MUX_INTERNAL_DEMOD 2
|
||
-#define MUX_CICAM 3
|
||
-#define MUX0_OFFS 0
|
||
-#define MUX1_OFFS 2
|
||
-#define MUX_CAM_OFFS 4
|
||
-
|
||
-/* TSC_IN_IFC_EXT and TSC_IN_IFC_CFG_INT*/
|
||
-#define TSIF_INPUT_ENABLE 0
|
||
-#define TSIF_INPUT_DISABLE 1
|
||
-
|
||
-#define TSIF_CLK_POL_OFFS 0
|
||
-#define TSIF_DATA_POL_OFFS 1
|
||
-#define TSIF_START_POL_OFFS 2
|
||
-#define TSIF_VALID_POL_OFFS 3
|
||
-#define TSIF_ERROR_POL_OFFS 4
|
||
-#define TSIF_SER_PAR_OFFS 5
|
||
-#define TSIF_REC_MODE_OFFS 6
|
||
-#define TSIF_DATA_SWAP_OFFS 8
|
||
-#define TSIF_DISABLE_OFFS 9
|
||
-#define TSIF_ERR_INSERT_OFFS 10
|
||
-
|
||
-/* TSC_FSM_STATE and TSC_FSM_STATE_MASK*/
|
||
-#define FSM_STATE_BUFFER_BEG 0
|
||
-#define FSM_STATE_BUFFER_END 3
|
||
-#define FSM_STATE_POLL_BEG 8
|
||
-#define FSM_STATE_POLL_END 10
|
||
-#define FSM_STATE_BYTE_BEG 12
|
||
-#define FSM_STATE_BYTE_END 13
|
||
-#define FSM_STATE_MEM_WR_BEG 16
|
||
-#define FSM_STATE_MEM_WR_END 17
|
||
-#define FSM_STATE_MEM_RD_BEG 20
|
||
-#define FSM_STATE_MEM_RD_END 21
|
||
-#define FSM_STATE_IO_RD_BEG 24
|
||
-#define FSM_STATE_IO_RD_END 25
|
||
-#define FSM_STATE_IO_WR_BEG 28
|
||
-#define FSM_STATE_IO_WR_END 29
|
||
-
|
||
-/* TSC_CAM_CMD */
|
||
-#define MEMORY_TRANSACTION 0
|
||
-#define IO_TRANSACTION 1
|
||
-#define WRITE_TRANSACTION 0
|
||
-#define READ_TRANSACTION 1
|
||
-#define SINGLE_BYTE_MODE 0
|
||
-#define BUFFER_MODE 1
|
||
-
|
||
-#define CAM_CMD_ADDR_SIZE_OFFS 0
|
||
-#define CAM_CMD_WR_DATA_OFFS 16
|
||
-#define CAM_CMD_IO_MEM_OFFS 24
|
||
-#define CAM_CMD_RD_WR_OFFS 25
|
||
-#define CAM_CMD_BUFF_MODE_OFFS 26
|
||
-#define CAM_CMD_ABORT 27
|
||
-
|
||
-/* TSC_STAT, TSC_IRQ_ENA and TSC_IRQ_CLR */
|
||
-#define CAM_IRQ_EOT_OFFS 0
|
||
-#define CAM_IRQ_POLL_OFFS 1
|
||
-#define CAM_IRQ_RATE_MISMATCH_OFFS 2
|
||
-#define CAM_IRQ_ERR_OFFS 3
|
||
-#define CAM_IRQ_ABORTED_OFFS 4
|
||
-
|
||
-/* TSC_CD_STAT */
|
||
-#define TSC_CD_STAT_INSERT 0x00
|
||
-#define TSC_CD_STAT_ERROR1 0x01
|
||
-#define TSC_CD_STAT_ERROR2 0x02
|
||
-#define TSC_CD_STAT_REMOVE 0x03
|
||
-
|
||
-#define TSC_CD_BEG 0
|
||
-#define TSC_CD_END 1
|
||
-
|
||
-/* TSC_CICAM_TSIF */
|
||
-#define TSC_CICAM_TSIF_OE_OFFS 0
|
||
-
|
||
-/* Data structures */
|
||
-
|
||
-/**
|
||
- * enum transaction_state - states for the transacation interrupt reason
|
||
- */
|
||
-enum transaction_state {
|
||
- BEFORE_TRANSACTION = 0,
|
||
- TRANSACTION_SUCCESS = 1,
|
||
- TRANSACTION_ERROR = -1,
|
||
- TRANSACTION_CARD_REMOVED = -2
|
||
-};
|
||
-
|
||
-/**
|
||
-* enum pcmcia_state - states for the pcmcia pinctrl states
|
||
-* Note: the numbers here corresponds to the numbers of enum tsc_cam_personality
|
||
-* in tsc.h file.
|
||
-*/
|
||
-enum pcmcia_state {
|
||
- PCMCIA_STATE_DISABLE = 0,
|
||
- PCMCIA_STATE_CI_CARD = 1,
|
||
- PCMCIA_STATE_CI_PLUS = 2,
|
||
- PCMCIA_STATE_PC_CARD = 3
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct iommu_info - manage all the iommu information
|
||
- *
|
||
- * @group: TSC IOMMU group.
|
||
- * @domain: TSC IOMMU domain.
|
||
- * @domain_num: TSC IOMMU domain number.
|
||
- * @partition_num: TSC iommu partition number.
|
||
- * @ion_client: TSC IOMMU client.
|
||
- * @iommu_group_name TSC IOMMU group name.
|
||
- */
|
||
-struct iommu_info {
|
||
- struct iommu_group *group;
|
||
- struct iommu_domain *domain;
|
||
- int domain_num;
|
||
- int partition_num;
|
||
- struct ion_client *ion_client;
|
||
- const char *iommu_group_name;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct pinctrl_current_state - represent which TLMM pins currently active
|
||
- *
|
||
- * @ts0: true if TS-in 0 is active, false otherwise.
|
||
- * @ts1: true if TS-in 1 is active, false otherwise.
|
||
- * @pcmcia_state: Represent the pcmcia pins state.
|
||
- */
|
||
-struct pinctrl_current_state {
|
||
- bool ts0;
|
||
- bool ts1;
|
||
- enum pcmcia_state pcmcia_state;
|
||
-};
|
||
-/**
|
||
- * struct pinctrl_info - manage all the pinctrl information
|
||
- *
|
||
- * @pinctrl: TSC pinctrl state holder.
|
||
- * @disable: pinctrl state to disable all the pins.
|
||
- * @ts0: pinctrl state to activate TS-in 0 alone.
|
||
- * @ts1: pinctrl state to activate TS-in 1 alone.
|
||
- * @dual_ts: pinctrl state to activate both TS-in.
|
||
- * @pc_card: pinctrl state to activate pcmcia upon card insertion.
|
||
- * @ci_card: pinctrl state to activate pcmcia after personality
|
||
- * change to CI card.
|
||
- * @ci_plus: pinctrl state to activate pcmcia after personality
|
||
- * change to CI+ card.
|
||
- * @ts0_pc_card: pinctrl state to activate TS-in 0 and pcmcia upon card
|
||
- * insertion.
|
||
- * @ts0_ci_card: pinctrl state to activate TS-in 0 and pcmcia after
|
||
- * personality change to CI card.
|
||
- * @ts0_ci_plus: pinctrl state to activate TS-in 0 and pcmcia after
|
||
- * personality change to CI+ card.
|
||
- * @ts1_pc_card: pinctrl state to activate TS-in 1 and pcmcia upon card
|
||
- * insertion.
|
||
- * @ts1_ci_card: pinctrl state to activate TS-in 1 and pcmcia after
|
||
- * personality change to CI card.
|
||
- * @ts1_ci_plus: pinctrl state to activate TS-in 1 and pcmcia after
|
||
- * personality change to CI+ card.
|
||
- * @dual_ts_pc_card: pinctrl state to activate both TS-in and pcmcia upon
|
||
- * card insertion.
|
||
- * @dual_ts_ci_card: pinctrl state to activate both TS-in and pcmcia after
|
||
- * personality change to CI card.
|
||
- * @dual_ts_ci_plus: pinctrl state to activate both TS-in and pcmcia after
|
||
- * personality change to CI+ card.
|
||
- * @is_ts0: true if ts0 pinctrl states exist in device tree, false
|
||
- * otherwise.
|
||
- * @is_ts1: true if ts1 pinctrl states exist in device tree, false
|
||
- * otherwise.
|
||
- * @is_pcmcia: true if pcmcia pinctrl states exist in device tree,
|
||
- * false otherwise.
|
||
- * @curr_state: the current state of the TLMM pins.
|
||
- */
|
||
-struct pinctrl_info {
|
||
- struct pinctrl *pinctrl;
|
||
- struct pinctrl_state *disable;
|
||
- struct pinctrl_state *ts0;
|
||
- struct pinctrl_state *ts1;
|
||
- struct pinctrl_state *dual_ts;
|
||
- struct pinctrl_state *pc_card;
|
||
- struct pinctrl_state *ci_card;
|
||
- struct pinctrl_state *ci_plus;
|
||
- struct pinctrl_state *ts0_pc_card;
|
||
- struct pinctrl_state *ts0_ci_card;
|
||
- struct pinctrl_state *ts0_ci_plus;
|
||
- struct pinctrl_state *ts1_pc_card;
|
||
- struct pinctrl_state *ts1_ci_card;
|
||
- struct pinctrl_state *ts1_ci_plus;
|
||
- struct pinctrl_state *dual_ts_pc_card;
|
||
- struct pinctrl_state *dual_ts_ci_card;
|
||
- struct pinctrl_state *dual_ts_ci_plus;
|
||
- bool is_ts0;
|
||
- bool is_ts1;
|
||
- bool is_pcmcia;
|
||
- struct pinctrl_current_state curr_state;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tsc_mux_chdev - TSC Mux character device
|
||
- *
|
||
- * @cdev: TSC Mux cdev.
|
||
- * @mutex: A mutex for mutual exclusion between Mux API calls.
|
||
- * @poll_queue: Waiting queue for rate mismatch interrupt.
|
||
- * @spinlock: A spinlock to protect accesses to
|
||
- * data structures that happen from APIs and ISRs.
|
||
- * @rate_interrupt: A flag indicating if rate mismatch interrupt received.
|
||
- */
|
||
-struct tsc_mux_chdev {
|
||
- struct cdev cdev;
|
||
- struct mutex mutex;
|
||
- wait_queue_head_t poll_queue;
|
||
- spinlock_t spinlock;
|
||
- bool rate_interrupt;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tsc_ci_chdev - TSC CI character device
|
||
- *
|
||
- * @cdev: TSC CI cdev.
|
||
- * @mutex: A mutex for mutual exclusion between CI API calls.
|
||
- * @poll_queue: Waiting queue for card detection interrupt.
|
||
- * @spinlock: A spinlock to protect accesses to data structures that
|
||
- * happen from APIs and ISRs.
|
||
- * @transaction_complete: A completion struct indicating end of data
|
||
- * transaction.
|
||
- * @transaction_finish: A completion struct indicating data transaction func
|
||
- * has finished.
|
||
- * @transaction_state: flag indicating the reason for transaction end.
|
||
- * @ci_card_status: The last card status received by the upper layer.
|
||
- * @data_busy: true when the device is in the middle of data
|
||
- * transaction operation, false otherwise.
|
||
- */
|
||
-struct tsc_ci_chdev {
|
||
- struct cdev cdev;
|
||
- struct mutex mutex;
|
||
- wait_queue_head_t poll_queue;
|
||
- spinlock_t spinlock;
|
||
- struct completion transaction_complete;
|
||
- struct completion transaction_finish;
|
||
- enum transaction_state transaction_state;
|
||
- enum tsc_card_status card_status;
|
||
- bool data_busy;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tsc_device - TSC device
|
||
- *
|
||
- * @pdev: TSC platform device.
|
||
- * @device_mux: Mux device for sysfs and /dev entry.
|
||
- * @device_ci: CI device for sysfs and /dev entry.
|
||
- * @mux_chdev: TSC Mux character device instance.
|
||
- * @ci_chdev: TSC CI character device instance.
|
||
- * @mux_device_number: TSC Mux major number.
|
||
- * @ci_device_number: TSC CI major number.
|
||
- * @num_mux_opened: A counter to ensure 1 TSC Mux character device.
|
||
- * @num_ci_opened: A counter to ensure 1 TSC CI character device.
|
||
- * @num_device_open: A counter to synch init of power and bus voting.
|
||
- * @mutex: Global mutex to to synch init of power and bus voting.
|
||
- * @base: Base memory address for the TSC registers.
|
||
- * @card_detection_irq: Interrupt No. of the card detection interrupt.
|
||
- * @cam_cmd_irq: Interrupt No. of the cam cmd interrupt.
|
||
- * @iommu_info: TSC IOMMU parameters.
|
||
- * @ahb_clk: The clock for accessing the TSC registers.
|
||
- * @ci_clk: The clock for TSC internal logic.
|
||
- * @ser_clk: The clock for synchronizing serial TS input.
|
||
- * @par_clk: The clock for synchronizing parallel TS input.
|
||
- * @cicam_ts_clk: The clock for pushing TS data into the cicam.
|
||
- * @tspp2_core_clk: The clock for enabling the TSPP2.
|
||
- * @vbif_tspp2_clk: The clock for accessing the VBIF.
|
||
- * @vbif_ahb_clk: The clock for VBIF AHB.
|
||
- * @vbif_axi_clk: The clock for VBIF AXI.
|
||
- * @gdsc: The Broadcast GDSC.
|
||
- * @bus_client: The TSC bus client.
|
||
- * @pinctrl_info: TSC pinctrl parameters.
|
||
- * @reset_cam_gpio: GPIO No. for CAM HW reset.
|
||
- * @hw_card_status: The card status as reflected by the HW registers.
|
||
- * @card_power: True if the card is powered up, false otherwise.
|
||
- * @debugfs_entry: TSC device debugfs entry.
|
||
- */
|
||
-struct tsc_device {
|
||
- struct platform_device *pdev;
|
||
- struct device *device_mux;
|
||
- struct device *device_ci;
|
||
- struct tsc_mux_chdev mux_chdev;
|
||
- struct tsc_ci_chdev ci_chdev;
|
||
- dev_t mux_device_number;
|
||
- dev_t ci_device_number;
|
||
- int num_mux_opened;
|
||
- int num_ci_opened;
|
||
- int num_device_open;
|
||
- struct mutex mutex;
|
||
- void __iomem *base;
|
||
- unsigned int card_detection_irq;
|
||
- unsigned int cam_cmd_irq;
|
||
- struct iommu_info iommu_info;
|
||
- struct clk *ahb_clk;
|
||
- struct clk *ci_clk;
|
||
- struct clk *ser_clk;
|
||
- struct clk *par_clk;
|
||
- struct clk *cicam_ts_clk;
|
||
- struct clk *tspp2_core_clk;
|
||
- struct clk *vbif_tspp2_clk;
|
||
- struct clk *vbif_ahb_clk;
|
||
- struct clk *vbif_axi_clk;
|
||
- struct regulator *gdsc;
|
||
- uint32_t bus_client;
|
||
- struct pinctrl_info pinctrl_info;
|
||
- int reset_cam_gpio;
|
||
- enum tsc_card_status hw_card_status;
|
||
- bool card_power;
|
||
- struct dentry *debugfs_entry;
|
||
-};
|
||
-
|
||
-/* Global TSC device class */
|
||
-static struct class *tsc_class;
|
||
-
|
||
-/* Global TSC device database */
|
||
-static struct tsc_device *tsc_device;
|
||
-
|
||
-/************************** Debugfs Support **************************/
|
||
-/* debugfs entries */
|
||
-#define TSC_S_RW (S_IRUGO | S_IWUSR)
|
||
-
|
||
-struct debugfs_entry {
|
||
- const char *name;
|
||
- mode_t mode;
|
||
- int offset;
|
||
-};
|
||
-
|
||
-static const struct debugfs_entry tsc_regs_32[] = {
|
||
- {"tsc_hw_version", S_IRUGO, TSC_HW_VERSION},
|
||
- {"tsc_mux", TSC_S_RW, TSC_MUX_CFG},
|
||
- {"tsif_external_demods", TSC_S_RW, TSC_IN_IFC_EXT},
|
||
- {"tsif_internal_demod_cam", TSC_S_RW, TSC_IN_IFC_CFG_INT},
|
||
- {"tsc_fsm_state", S_IRUGO, TSC_FSM_STATE},
|
||
- {"tsc_fsm_state_mask", TSC_S_RW, TSC_FSM_STATE_MASK},
|
||
- {"tsc_cam_cmd", TSC_S_RW, TSC_CAM_CMD},
|
||
- {"tsc_rd_buff_addr", TSC_S_RW, TSC_RD_BUFF_ADDR},
|
||
- {"tsc_wr_buff_addr", TSC_S_RW, TSC_WR_BUFF_ADDR},
|
||
-};
|
||
-
|
||
-static const struct debugfs_entry tsc_regs_16[] = {
|
||
- {"tsc_false_cd_counter", S_IRUGO, TSC_FALSE_CD},
|
||
- {"tsc_cicam_tsif", TSC_S_RW, TSC_CICAM_TSIF},
|
||
-};
|
||
-
|
||
-static const struct debugfs_entry tsc_regs_8[] = {
|
||
- {"tsc_cam_rd_data", S_IRUGO, TSC_CAM_RD_DATA},
|
||
- {"tsc_irq_stat", S_IRUGO, TSC_STAT},
|
||
- {"tsc_irq_ena", TSC_S_RW, TSC_IRQ_ENA},
|
||
- {"tsc_irq_clr", TSC_S_RW, TSC_IRQ_CLR},
|
||
- {"tsc_ena_hw_poll", TSC_S_RW, TSC_CIP_CFG},
|
||
- {"tsc_card_stat", TSC_S_RW, TSC_CD_STAT},
|
||
- {"tsc_false_cd_counter_clr", TSC_S_RW, TSC_FALSE_CD_CLR},
|
||
- {"tsc_last_error_resp", S_IRUGO, TSC_RESP_ERR},
|
||
-};
|
||
-
|
||
-/* debugfs settings */
|
||
-static int debugfs_iomem_set(void *data, u64 val)
|
||
-{
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (!tsc_device->num_device_open) {
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- writel_relaxed(val, data);
|
||
- wmb();
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-static int debugfs_iomem_get(void *data, u64 *val)
|
||
-{
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (!tsc_device->num_device_open) {
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- *val = readl_relaxed(data);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_get,
|
||
- debugfs_iomem_set, "0x%08llX");
|
||
-DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x16, debugfs_iomem_get,
|
||
- debugfs_iomem_set, "0x%04llX");
|
||
-DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x8, debugfs_iomem_get,
|
||
- debugfs_iomem_set, "0x%02llX");
|
||
-
|
||
-/**
|
||
- * tsc_debugfs_init() - TSC device debugfs initialization.
|
||
- */
|
||
-static void tsc_debugfs_init(void)
|
||
-{
|
||
- int i;
|
||
- struct dentry *dentry;
|
||
- void __iomem *base = tsc_device->base;
|
||
-
|
||
- tsc_device->debugfs_entry = debugfs_create_dir("tsc", NULL);
|
||
- if (!tsc_device->debugfs_entry)
|
||
- return;
|
||
- dentry = debugfs_create_dir("regs", tsc_device->debugfs_entry);
|
||
- if (dentry) {
|
||
- for (i = 0; i < ARRAY_SIZE(tsc_regs_32); i++) {
|
||
- debugfs_create_file(
|
||
- tsc_regs_32[i].name,
|
||
- tsc_regs_32[i].mode,
|
||
- dentry,
|
||
- base + tsc_regs_32[i].offset,
|
||
- &fops_iomem_x32);
|
||
- }
|
||
- for (i = 0; i < ARRAY_SIZE(tsc_regs_16); i++) {
|
||
- debugfs_create_file(
|
||
- tsc_regs_16[i].name,
|
||
- tsc_regs_16[i].mode,
|
||
- dentry,
|
||
- base + tsc_regs_16[i].offset,
|
||
- &fops_iomem_x16);
|
||
- }
|
||
- for (i = 0; i < ARRAY_SIZE(tsc_regs_8); i++) {
|
||
- debugfs_create_file(
|
||
- tsc_regs_8[i].name,
|
||
- tsc_regs_8[i].mode,
|
||
- dentry,
|
||
- base + tsc_regs_8[i].offset,
|
||
- &fops_iomem_x8);
|
||
- }
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_debugfs_exit() - TSC device debugfs teardown.
|
||
- */
|
||
-static void tsc_debugfs_exit(void)
|
||
-{
|
||
- debugfs_remove_recursive(tsc_device->debugfs_entry);
|
||
- tsc_device->debugfs_entry = NULL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_update_hw_card_status() - Update the hw_status according to the HW reg.
|
||
- *
|
||
- * Read the register indicating the card status (inserted, removed, error) and
|
||
- * update the tsc_device->hw_card_status accordingly.
|
||
- */
|
||
-static void tsc_update_hw_card_status(void)
|
||
-{
|
||
- u32 cd_reg, card_status = 0;
|
||
-
|
||
- cd_reg = readl_relaxed(tsc_device->base + TSC_CD_STAT);
|
||
- card_status = GETL_BITS(cd_reg, TSC_CD_BEG, TSC_CD_END);
|
||
- switch (card_status) {
|
||
- case TSC_CD_STAT_INSERT:
|
||
- tsc_device->hw_card_status = TSC_CARD_STATUS_DETECTED;
|
||
- break;
|
||
- case TSC_CD_STAT_ERROR1:
|
||
- case TSC_CD_STAT_ERROR2:
|
||
- tsc_device->hw_card_status = TSC_CARD_STATUS_FAILURE;
|
||
- break;
|
||
- case TSC_CD_STAT_REMOVE:
|
||
- tsc_device->hw_card_status = TSC_CARD_STATUS_NOT_DETECTED;
|
||
- break;
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_card_power_down() - power down card interface upon removal.
|
||
- *
|
||
- * Power down the card by disable VPP, disable pins in the TLMM, assert the
|
||
- * reset line and disable the level-shifters. This function assumes the spinlock
|
||
- * of ci device is already taken.
|
||
- *
|
||
- * Return 0 on finish, error value if interrupted while acquiring a mutex.
|
||
- */
|
||
-static int tsc_card_power_down(void)
|
||
-{
|
||
- int ret = 0;
|
||
- struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
|
||
- struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
|
||
- int reset_gpio = tsc_device->reset_cam_gpio;
|
||
- u32 reg = 0;
|
||
-
|
||
- /* Clearing CAM TSIF OE to disable I/O CAM transactions */
|
||
- CLEAR_BIT(TSC_CICAM_TSIF_OE_OFFS, reg);
|
||
- writel_relaxed(reg, tsc_device->base + TSC_CICAM_TSIF);
|
||
-
|
||
- /* Assert the reset line */
|
||
- ret = gpio_direction_output(reset_gpio, 1); /* assert */
|
||
- if (ret != 0)
|
||
- pr_err("%s: Failed to assert the reset CAM GPIO\n", __func__);
|
||
-
|
||
- /* Disable all the level-shifters */
|
||
- /* TODO: call mpq_standby_pcmcia_master0_set(0) after MCU mainlined */
|
||
- if (ret != 0)
|
||
- pr_err("%s: error disable master0 level-shifters. ret value = %d\n",
|
||
- __func__, ret);
|
||
- /* TODO: call mpq_standby_pcmcia_master1_set(1) after MCU mainlined */
|
||
- if (ret != 0)
|
||
- pr_err("%s: error disable master1 level-shifters. ret value = %d\n",
|
||
- __func__, ret);
|
||
-
|
||
- /* Power-down the card */
|
||
- /* TODO: call mpq_standby_pcmcia_vpp_set(1) after MCU mainlined */
|
||
- if (ret != 0)
|
||
- pr_err("%s: error disabling VPP. ret value = %d\n", __func__,
|
||
- ret);
|
||
- /* Wait 10msec until VPP become stable */
|
||
- usleep(10000);
|
||
-
|
||
- /* Disable pins in the TLMM */
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts);
|
||
- else if (pcurr_state->ts0 && !pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0);
|
||
- else if (!pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->disable);
|
||
- if (ret != 0)
|
||
- pr_err("%s: error changing PCMCIA pins upon card removal. ret value = %d\n",
|
||
- __func__, ret);
|
||
- else
|
||
- pcurr_state->pcmcia_state = PCMCIA_STATE_DISABLE;
|
||
-
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_card_power_up() - power up card interface upon insertion.
|
||
- *
|
||
- * Power up the card by open VPP, enable pins in the TLMM, deassert the reset
|
||
- * line and enable the level-shifters. This function assumes the spinlock of ci
|
||
- * device is already taken.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_card_power_up(void)
|
||
-{
|
||
- int ret = 0;
|
||
- struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
|
||
- struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
|
||
- int reset_gpio = tsc_device->reset_cam_gpio;
|
||
-
|
||
- /* Power-up the card */
|
||
- /* TODO: call mpq_standby_pcmcia_vpp_set(1) after MCU mainlined */
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error setting VPP. ret value = %d\n", __func__,
|
||
- ret);
|
||
- return ret;
|
||
- }
|
||
- /* Wait 10msec until VPP become stable */
|
||
- usleep(10000);
|
||
-
|
||
- /* Enable pins in the TLMM */
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_pc_card);
|
||
- else if (pcurr_state->ts0 && !pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_pc_card);
|
||
- else if (!pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_pc_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->pc_card);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error changing PCMCIA pins upon card insertion. ret value = %d\n",
|
||
- __func__, ret);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- goto err;
|
||
- } else {
|
||
- pcurr_state->pcmcia_state = PCMCIA_STATE_PC_CARD;
|
||
- }
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- /* Release the reset line */
|
||
- ret = gpio_direction_output(reset_gpio, 0); /* Deassert */
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Failed to deassert the reset CAM GPIO\n", __func__);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- /* Enable level-shifters for all pins */
|
||
- /* TODO: call mpq_standby_pcmcia_master0_set(0) after MCU mainlined */
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error setting master0 level-shifters. ret value = %d\n",
|
||
- __func__, ret);
|
||
- goto err;
|
||
- }
|
||
- /* TODO: call mpq_standby_pcmcia_master1_set(0) after MCU mainlined */
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error setting master1 level-shifters. ret value = %d\n",
|
||
- __func__, ret);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- /* Wait 20msec at the end of the power-up sequence */
|
||
- usleep(20000);
|
||
-
|
||
- return ret;
|
||
-
|
||
-err:
|
||
- tsc_card_power_down();
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/************************** Interrupt handlers **************************/
|
||
-/**
|
||
- * tsc_card_detect_irq_thread_handler() - TSC card detect interrupt handler.
|
||
- *
|
||
- * @irq: Interrupt number.
|
||
- * @dev: TSC device.
|
||
- *
|
||
- * The handler is executed on a thread context, not in the interrupt context
|
||
- * (can take a mutex and sleep).
|
||
- * Read the card detection status from the register and initiate a power-up/down
|
||
- * sequence accordingly. The sequence will occur only if a change is needed in
|
||
- * the current power state.
|
||
- *
|
||
- */
|
||
-static irqreturn_t tsc_card_detect_irq_thread_handler(int irq, void *dev)
|
||
-{
|
||
- int ret = 0;
|
||
- struct tsc_ci_chdev *tsc_ci;
|
||
- unsigned long flags = 0;
|
||
-
|
||
- tsc_ci = &tsc_device->ci_chdev;
|
||
-
|
||
- mutex_lock(&tsc_ci->mutex);
|
||
-
|
||
- tsc_update_hw_card_status();
|
||
-
|
||
- /* waking-up ci poll queue */
|
||
- wake_up_interruptible(&tsc_ci->poll_queue);
|
||
-
|
||
- /* If in the middle of a data transaction- aborting the transaction */
|
||
- if (tsc_ci->data_busy && tsc_device->hw_card_status ==
|
||
- TSC_CARD_STATUS_NOT_DETECTED) {
|
||
- spin_lock_irqsave(&tsc_ci->spinlock, flags);
|
||
- tsc_ci->transaction_state = TRANSACTION_CARD_REMOVED;
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
- complete_all(&tsc_ci->transaction_complete);
|
||
- }
|
||
-
|
||
- if (tsc_device->hw_card_status == TSC_CARD_STATUS_DETECTED &&
|
||
- !tsc_device->card_power) {
|
||
- ret = tsc_card_power_up();
|
||
- if (ret != 0)
|
||
- pr_err("%s: card power-up failed\n", __func__);
|
||
- else
|
||
- tsc_device->card_power = true;
|
||
- } else if (tsc_device->hw_card_status == TSC_CARD_STATUS_NOT_DETECTED &&
|
||
- tsc_device->card_power) {
|
||
- tsc_card_power_down();
|
||
- /*
|
||
- * In case something failed during the power down, the sequence
|
||
- * continue and the status of the card power is considered as
|
||
- * powered down.
|
||
- */
|
||
- tsc_device->card_power = false;
|
||
- }
|
||
-
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
-
|
||
- return IRQ_HANDLED;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_cam_cmd_irq_handler() - TSC CAM interrupt handler.
|
||
- *
|
||
- * @irq: Interrupt number.
|
||
- * @dev: TSC device.
|
||
- *
|
||
- * Handle TSC CAM HW interrupt. Handle the CAM transaction interrupts by waking
|
||
- * up the completion sync object, handle rate mismatch interrupt by waking-up
|
||
- * the TSC Mux poll wait-queue and clear the interrupts received.
|
||
- *
|
||
- * Return IRQ_HANDLED.
|
||
- */
|
||
-static irqreturn_t tsc_cam_cmd_irq_handler(int irq, void *dev)
|
||
-{
|
||
- struct tsc_ci_chdev *tsc_ci;
|
||
- struct tsc_mux_chdev *tsc_mux;
|
||
- unsigned long flags;
|
||
- u32 stat_reg, ena_reg;
|
||
-
|
||
- tsc_ci = &tsc_device->ci_chdev;
|
||
- tsc_mux = &tsc_device->mux_chdev;
|
||
-
|
||
- stat_reg = readl_relaxed(tsc_device->base + TSC_STAT);
|
||
-
|
||
- /* Handling transaction interrupts */
|
||
- if (TEST_BIT(CAM_IRQ_ERR_OFFS, stat_reg) ||
|
||
- TEST_BIT(CAM_IRQ_EOT_OFFS, stat_reg)) {
|
||
- spin_lock_irqsave(&tsc_ci->spinlock, flags);
|
||
-
|
||
- if (TEST_BIT(CAM_IRQ_EOT_OFFS, stat_reg))
|
||
- tsc_ci->transaction_state = TRANSACTION_SUCCESS;
|
||
- else
|
||
- tsc_ci->transaction_state = TRANSACTION_ERROR;
|
||
-
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
- complete_all(&tsc_ci->transaction_complete);
|
||
- }
|
||
-
|
||
- /* Handling rate mismatch interrupt */
|
||
- if (TEST_BIT(CAM_IRQ_RATE_MISMATCH_OFFS, stat_reg)) {
|
||
- spin_lock_irqsave(&tsc_mux->spinlock, flags);
|
||
-
|
||
- /* Disabling rate mismatch interrupt */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- CLEAR_BIT(CAM_IRQ_RATE_MISMATCH_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- /* Setting internal flag for poll */
|
||
- tsc_mux->rate_interrupt = true;
|
||
-
|
||
- spin_unlock_irqrestore(&tsc_mux->spinlock, flags);
|
||
- /* waking-up mux poll queue */
|
||
- wake_up_interruptible(&tsc_mux->poll_queue);
|
||
- }
|
||
-
|
||
- /* Clearing all the interrupts received */
|
||
- writel_relaxed(stat_reg, tsc_device->base + TSC_IRQ_CLR);
|
||
-
|
||
- /*
|
||
- * Before returning IRQ_HANDLED to the generic interrupt handling
|
||
- * framework need to make sure all operations including clearing of
|
||
- * interrupt status registers in the hardware is performed.
|
||
- * Thus a barrier after clearing the interrupt status register
|
||
- * is required to guarantee that the interrupt status register has
|
||
- * really been cleared by the time we return from this handler.
|
||
- */
|
||
- wmb();
|
||
-
|
||
- return IRQ_HANDLED;
|
||
-}
|
||
-
|
||
-/************************** Internal functions **************************/
|
||
-
|
||
-/**
|
||
- * tsc_set_cicam_clk() - Setting the rate of the TS from the TSC to the CAM
|
||
- *
|
||
- * @arg: The argument received from the user-space via set rate IOCTL.
|
||
- * It is the value of the requested rate in MHz.
|
||
- *
|
||
- * Setting the rate of the cicam_ts_clk clock, with one of the valid clock
|
||
- * frequencies. The arg value given is rounded to the nearest frequency.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_set_cicam_clk(unsigned long arg)
|
||
-{
|
||
- int ret;
|
||
-
|
||
- if (arg <= 8)
|
||
- ret = clk_set_rate(tsc_device->cicam_ts_clk,
|
||
- CICAM_CLK_RATE_7MHZ);
|
||
- else if (arg <= 11)
|
||
- ret = clk_set_rate(tsc_device->cicam_ts_clk,
|
||
- CICAM_CLK_RATE_9MHZ);
|
||
- else
|
||
- ret = clk_set_rate(tsc_device->cicam_ts_clk,
|
||
- CICAM_CLK_RATE_12MHZ);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_enable_rate_irq() - Enabling the rate mismatch interrupt.
|
||
- *
|
||
- * @tsc_mux: TSC Mux device.
|
||
- *
|
||
- * Setting the bit of this interrupt in the register that controls which
|
||
- * interrupts are enabled.
|
||
- */
|
||
-static void tsc_enable_rate_irq(struct tsc_mux_chdev *tsc_mux)
|
||
-{
|
||
- unsigned long flags;
|
||
- u32 ena_reg = 0;
|
||
-
|
||
- spin_lock_irqsave(&tsc_mux->spinlock, flags);
|
||
-
|
||
- /* Setting the bit to start receiving rate mismatch interrupt again */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- SET_BIT(CAM_IRQ_RATE_MISMATCH_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- spin_unlock_irqrestore(&tsc_mux->spinlock, flags);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_config_tsif() - Modifying TSIF configuration.
|
||
- *
|
||
- * @tsc_mux: TSC Mux device.
|
||
- * @tsif_params: TSIF parameters received from the user-space via IOCTL.
|
||
- *
|
||
- * Update the specified TSIF parameters according to the values in tsif_params.
|
||
- * The update is done by modifying a HW register.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_config_tsif(struct tsc_mux_chdev *tsc_mux,
|
||
- struct tsc_tsif_params *tsif_params)
|
||
-{
|
||
- int ret = 0;
|
||
- u32 reg;
|
||
- int reg_internal_offs;
|
||
- u32 reg_addr_offs;
|
||
-
|
||
- switch (tsif_params->source) {
|
||
- case TSC_SOURCE_EXTERNAL0:
|
||
- reg_internal_offs = 0;
|
||
- reg_addr_offs = TSC_IN_IFC_EXT;
|
||
- break;
|
||
- case TSC_SOURCE_EXTERNAL1:
|
||
- reg_internal_offs = 16;
|
||
- reg_addr_offs = TSC_IN_IFC_EXT;
|
||
- break;
|
||
- case TSC_SOURCE_INTERNAL:
|
||
- reg_internal_offs = 0;
|
||
- reg_addr_offs = TSC_IN_IFC_CFG_INT;
|
||
- break;
|
||
- case TSC_SOURCE_CICAM:
|
||
- reg_internal_offs = 16;
|
||
- reg_addr_offs = TSC_IN_IFC_CFG_INT;
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: unidentified source parameter\n", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err;
|
||
- }
|
||
-
|
||
-
|
||
- reg = readl_relaxed(tsc_device->base + reg_addr_offs);
|
||
-
|
||
- /* Modifying TSIF settings in the register value */
|
||
- (tsif_params->clock_polarity ?
|
||
- SET_BIT((reg_internal_offs + TSIF_CLK_POL_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_CLK_POL_OFFS), reg));
|
||
- (tsif_params->data_polarity ?
|
||
- SET_BIT(((reg_internal_offs + TSIF_DATA_POL_OFFS)), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_DATA_POL_OFFS), reg));
|
||
- (tsif_params->start_polarity ?
|
||
- SET_BIT((reg_internal_offs + TSIF_START_POL_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_START_POL_OFFS), reg));
|
||
- (tsif_params->valid_polarity ?
|
||
- SET_BIT((reg_internal_offs + TSIF_VALID_POL_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_VALID_POL_OFFS), reg));
|
||
- (tsif_params->error_polarity ?
|
||
- SET_BIT((reg_internal_offs + TSIF_ERROR_POL_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_ERROR_POL_OFFS), reg));
|
||
- (tsif_params->data_type ?
|
||
- SET_BIT((reg_internal_offs + TSIF_SER_PAR_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_SER_PAR_OFFS), reg));
|
||
- reg &= ~(0x3 << TSIF_REC_MODE_OFFS);
|
||
- reg |= (tsif_params->receive_mode << TSIF_REC_MODE_OFFS);
|
||
- (tsif_params->data_swap ?
|
||
- SET_BIT((reg_internal_offs + TSIF_DATA_SWAP_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_DATA_SWAP_OFFS), reg));
|
||
- (tsif_params->set_error ?
|
||
- SET_BIT((reg_internal_offs + TSIF_ERR_INSERT_OFFS), reg) :
|
||
- CLEAR_BIT((reg_internal_offs + TSIF_ERR_INSERT_OFFS), reg));
|
||
-
|
||
- /* Writing the new settings to the register */
|
||
- writel_relaxed(reg, tsc_device->base + reg_addr_offs);
|
||
-
|
||
-err:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_suspend_ts_pins() - Suspend TS-in pins
|
||
- *
|
||
- * @source: The TSIF to configure.
|
||
- *
|
||
- * Config the TLMM pins of a TSIF as TS-in pins in sleep state according to
|
||
- * the current pinctrl configuration of the other pins.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_suspend_ts_pins(enum tsc_source source)
|
||
-{
|
||
- int ret = 0;
|
||
- struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
|
||
- struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (source == TSC_SOURCE_EXTERNAL0) {
|
||
- if (!ppinctrl->is_ts0) {
|
||
- pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n",
|
||
- __func__);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Transition from current pinctrl state to curr + ts0 sleep */
|
||
- switch (pcurr_state->pcmcia_state) {
|
||
- case PCMCIA_STATE_DISABLE:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->disable);
|
||
- break;
|
||
- case PCMCIA_STATE_PC_CARD:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_pc_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->pc_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_CARD:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_PLUS:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_plus);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_plus);
|
||
- break;
|
||
- }
|
||
- } else { /* source == TSC_SOURCE_EXTERNAL1 */
|
||
- if (!ppinctrl->is_ts1) {
|
||
- pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n",
|
||
- __func__);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Transition from current pinctrl state to curr + ts1 sleep */
|
||
- switch (pcurr_state->pcmcia_state) {
|
||
- case PCMCIA_STATE_DISABLE:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->disable);
|
||
- break;
|
||
- case PCMCIA_STATE_PC_CARD:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_pc_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->pc_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_CARD:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_PLUS:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_plus);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_plus);
|
||
- break;
|
||
- }
|
||
- }
|
||
-
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error disabling TS-in pins. ret value = %d\n",
|
||
- __func__, ret);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Update the current pinctrl state in the internal struct */
|
||
- if (source == TSC_SOURCE_EXTERNAL0)
|
||
- pcurr_state->ts0 = false;
|
||
- else
|
||
- pcurr_state->ts1 = false;
|
||
-
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_activate_ts_pins() - Activate TS-in pins
|
||
- *
|
||
- * @source: The TSIF to configure.
|
||
- *
|
||
- * Config the TLMM pins of a TSIF as TS-in pins in active state according to
|
||
- * the current pinctrl configuration of the other pins
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_activate_ts_pins(enum tsc_source source)
|
||
-{
|
||
- int ret = 0;
|
||
- struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
|
||
- struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (source == TSC_SOURCE_EXTERNAL0) {
|
||
- if (!ppinctrl->is_ts0) {
|
||
- pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n",
|
||
- __func__);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Transition from current pinctrl state to curr + ts0 active */
|
||
- switch (pcurr_state->pcmcia_state) {
|
||
- case PCMCIA_STATE_DISABLE:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0);
|
||
- break;
|
||
- case PCMCIA_STATE_PC_CARD:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_pc_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_pc_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_CARD:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_PLUS:
|
||
- if (pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_plus);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_plus);
|
||
- break;
|
||
- }
|
||
- } else { /* source == TSC_SOURCE_EXTERNAL1 */
|
||
- if (!ppinctrl->is_ts1) {
|
||
- pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n",
|
||
- __func__);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Transition from current pinctrl state to curr + ts1 active */
|
||
- switch (pcurr_state->pcmcia_state) {
|
||
- case PCMCIA_STATE_DISABLE:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1);
|
||
- break;
|
||
- case PCMCIA_STATE_PC_CARD:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_pc_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_pc_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_CARD:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_card);
|
||
- break;
|
||
- case PCMCIA_STATE_CI_PLUS:
|
||
- if (pcurr_state->ts0)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_plus);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_plus);
|
||
- break;
|
||
- }
|
||
- }
|
||
-
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error activating TS-in pins. ret value = %d\n",
|
||
- __func__, ret);
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Update the current pinctrl state in the internal struct */
|
||
- if (source == TSC_SOURCE_EXTERNAL0)
|
||
- pcurr_state->ts0 = true;
|
||
- else
|
||
- pcurr_state->ts1 = true;
|
||
-
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_enable_disable_tsif() - Enable/disable a TSIF.
|
||
- *
|
||
- * @tsc_mux: TSC Mux device.
|
||
- * @source: The TSIF to enable or disable.
|
||
- * @operation: The operation to perform: 0- enable, 1- disable.
|
||
- *
|
||
- * Enable or disable the specified TSIF, which consequently will block the TS
|
||
- * flowing through this TSIF. The update is done by modifying a HW register.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_enable_disable_tsif(struct tsc_mux_chdev *tsc_mux,
|
||
- enum tsc_source source, int operation)
|
||
-{
|
||
- int ret = 0;
|
||
- u32 reg;
|
||
- u32 addr_offs;
|
||
- int reg_offs;
|
||
- int curr_disable_state;
|
||
-
|
||
- switch (source) {
|
||
- case TSC_SOURCE_EXTERNAL0:
|
||
- reg_offs = 0;
|
||
- addr_offs = TSC_IN_IFC_EXT;
|
||
- break;
|
||
- case TSC_SOURCE_EXTERNAL1:
|
||
- reg_offs = 16;
|
||
- addr_offs = TSC_IN_IFC_EXT;
|
||
- break;
|
||
- case TSC_SOURCE_INTERNAL:
|
||
- reg_offs = 0;
|
||
- addr_offs = TSC_IN_IFC_CFG_INT;
|
||
- break;
|
||
- case TSC_SOURCE_CICAM:
|
||
- reg_offs = 16;
|
||
- addr_offs = TSC_IN_IFC_CFG_INT;
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: unidentified source parameter\n", __func__);
|
||
- ret = -EINVAL;
|
||
- return ret;
|
||
- }
|
||
-
|
||
- /* Reading the current enable/disable state from the register */
|
||
- reg = readl_relaxed(tsc_device->base + addr_offs);
|
||
- curr_disable_state = GETL_BITS(reg, TSIF_DISABLE_OFFS + reg_offs,
|
||
- TSIF_DISABLE_OFFS + reg_offs);
|
||
- /* If the current state equals the new state- return success */
|
||
- if (curr_disable_state == operation)
|
||
- return ret;
|
||
-
|
||
- if (operation == TSIF_INPUT_DISABLE) {
|
||
- if (source == TSC_SOURCE_EXTERNAL0 ||
|
||
- source == TSC_SOURCE_EXTERNAL1) {
|
||
- /* Disabling the TS-in pins in the TLMM */
|
||
- ret = tsc_suspend_ts_pins(source);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Error suspending TS-in pins",
|
||
- __func__);
|
||
- return ret;
|
||
- }
|
||
- }
|
||
- SET_BIT((reg_offs + TSIF_DISABLE_OFFS), reg);
|
||
- } else {
|
||
- if (source == TSC_SOURCE_EXTERNAL0 ||
|
||
- source == TSC_SOURCE_EXTERNAL1) {
|
||
- /* Enabling the TS-in pins in the TLMM */
|
||
- ret = tsc_activate_ts_pins(source);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Error activating TS-in pins",
|
||
- __func__);
|
||
- return ret;
|
||
- }
|
||
- }
|
||
- CLEAR_BIT((reg_offs + TSIF_DISABLE_OFFS), reg);
|
||
- }
|
||
-
|
||
- /* Writing back to the reg the enable/disable of the TSIF */
|
||
- writel_relaxed(reg, tsc_device->base + addr_offs);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_route_mux() - Configuring one of the TSC muxes.
|
||
- *
|
||
- * @tsc_mux: TSC Mux device.
|
||
- * @source: The requested TS source to be selected by the mux.
|
||
- * @dest: The requested mux.
|
||
- *
|
||
- * Configuring the specified mux to pass the TS indicated by the src parameter.
|
||
- * The update is done by modifying a HW register.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_route_mux(struct tsc_mux_chdev *tsc_mux, enum tsc_source source,
|
||
- enum tsc_dest dest)
|
||
-{
|
||
- int ret = 0;
|
||
- u32 mux_cfg_reg;
|
||
- int src_val;
|
||
-
|
||
- switch (source) {
|
||
- case TSC_SOURCE_EXTERNAL0:
|
||
- src_val = MUX_EXTERNAL_DEMOD_0;
|
||
- break;
|
||
- case TSC_SOURCE_EXTERNAL1:
|
||
- src_val = MUX_EXTERNAL_DEMOD_1;
|
||
- break;
|
||
- case TSC_SOURCE_INTERNAL:
|
||
- src_val = MUX_INTERNAL_DEMOD;
|
||
- break;
|
||
- case TSC_SOURCE_CICAM:
|
||
- src_val = MUX_CICAM;
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: unidentified source parameter\n", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err;
|
||
- }
|
||
-
|
||
- /* Reading the current muxes state, to change only the requested mux */
|
||
- mux_cfg_reg = readl_relaxed(tsc_device->base + TSC_MUX_CFG);
|
||
-
|
||
- switch (dest) {
|
||
- case TSC_DEST_TSPP0:
|
||
- mux_cfg_reg &= ~(0x3 << MUX0_OFFS);
|
||
- mux_cfg_reg |= (src_val << MUX0_OFFS);
|
||
- break;
|
||
- case TSC_DEST_TSPP1:
|
||
- mux_cfg_reg &= ~(0x3 << MUX1_OFFS);
|
||
- mux_cfg_reg |= (src_val << MUX1_OFFS);
|
||
- break;
|
||
- case TSC_DEST_CICAM:
|
||
- if (src_val == TSC_SOURCE_CICAM) {
|
||
- pr_err("%s: Error: CICAM cannot be source and dest\n",
|
||
- __func__);
|
||
- ret = -EINVAL;
|
||
- goto err;
|
||
- }
|
||
- mux_cfg_reg &= ~(0x3 << MUX_CAM_OFFS);
|
||
- mux_cfg_reg |= (src_val << MUX_CAM_OFFS);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: unidentified dest parameter\n", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err;
|
||
- }
|
||
-
|
||
- writel_relaxed(mux_cfg_reg, tsc_device->base + TSC_MUX_CFG);
|
||
-
|
||
-err:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * is_tsc_idle() - Checking if TSC is idle.
|
||
- *
|
||
- * @tsc_ci: TSC CI device.
|
||
- *
|
||
- * Reading the TSC state-machine register and checking if the TSC is busy in
|
||
- * one of the operations reflected by this register.
|
||
- *
|
||
- * Return true if the TSC is idle and false if it's busy.
|
||
- */
|
||
-static bool is_tsc_idle(struct tsc_ci_chdev *tsc_ci)
|
||
-{
|
||
- u32 fsm_reg;
|
||
-
|
||
- fsm_reg = readl_relaxed(tsc_device->base + TSC_FSM_STATE);
|
||
- if (GETL_BITS(fsm_reg, FSM_STATE_BUFFER_BEG, FSM_STATE_BUFFER_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_POLL_BEG, FSM_STATE_POLL_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_BYTE_BEG, FSM_STATE_BYTE_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_MEM_WR_BEG,
|
||
- FSM_STATE_MEM_WR_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_MEM_RD_BEG,
|
||
- FSM_STATE_MEM_RD_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_IO_RD_BEG, FSM_STATE_IO_RD_END) ||
|
||
- GETL_BITS(fsm_reg, FSM_STATE_IO_WR_BEG, FSM_STATE_IO_WR_END) ||
|
||
- tsc_ci->data_busy)
|
||
- return false;
|
||
-
|
||
- tsc_ci->data_busy = true;
|
||
-
|
||
- return true;
|
||
-}
|
||
-
|
||
-
|
||
-/**
|
||
- * tsc_power_on_buff_mode_clocks() - power-on the TSPP2 and VBIF clocks.
|
||
- *
|
||
- * Power-on the TSPP2 and the VBIF clocks required for buffer mode transaction.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_power_on_buff_mode_clocks(void)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- ret = clk_prepare_enable(tsc_device->tspp2_core_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tspp2_core_clk", __func__);
|
||
- goto err_tspp2;
|
||
- }
|
||
- ret = clk_prepare_enable(tsc_device->vbif_tspp2_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start vbif_tspp2_clk", __func__);
|
||
- goto err_vbif_tspp2;
|
||
- }
|
||
- ret = clk_prepare_enable(tsc_device->vbif_ahb_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start vbif_ahb_clk", __func__);
|
||
- goto err_vbif_ahb;
|
||
- }
|
||
- ret = clk_prepare_enable(tsc_device->vbif_axi_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start vbif_axi_clk", __func__);
|
||
- goto err_vbif_axi;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_vbif_axi:
|
||
- clk_disable_unprepare(tsc_device->vbif_ahb_clk);
|
||
-err_vbif_ahb:
|
||
- clk_disable_unprepare(tsc_device->vbif_tspp2_clk);
|
||
-err_vbif_tspp2:
|
||
- clk_disable_unprepare(tsc_device->tspp2_core_clk);
|
||
-err_tspp2:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_power_off_buff_mode_clocks() - power-off the SPP2 and VBIF clocks.
|
||
- *
|
||
- * Power-off the TSPP2 and the VBIF clocks required for buffer mode transaction.
|
||
- */
|
||
-static void tsc_power_off_buff_mode_clocks(void)
|
||
-{
|
||
- clk_disable_unprepare(tsc_device->vbif_axi_clk);
|
||
- clk_disable_unprepare(tsc_device->vbif_ahb_clk);
|
||
- clk_disable_unprepare(tsc_device->tspp2_core_clk);
|
||
- clk_disable_unprepare(tsc_device->vbif_tspp2_clk);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_config_cam_data_transaction() - Configuring a new data transaction.
|
||
- *
|
||
- * @addr_size: The value for the address_size register field- address when
|
||
- * using single byte-mode, and size when using buffer mode.
|
||
- * @wr_data: the value for the wr_data register field- data to write to the
|
||
- * cam when using single byte mode.
|
||
- * @io_mem: The value for the io_mem register field- 1 for IO transaction,
|
||
- * 0 for memory transaction.
|
||
- * @read_write: The value for the read_write register field- 1 for read
|
||
- * transaction, 0 for write transaction.
|
||
- * @buff_mode: The value for the buff_mode register field- 1 for buffer mode,
|
||
- * 0 for single byte mode.
|
||
- *
|
||
- * Configuring the cam cmd register with the specified parameters, to initiate
|
||
- * data transaction with the cam.
|
||
- */
|
||
-static void tsc_config_cam_data_transaction(u16 addr_size,
|
||
- u8 wr_data,
|
||
- uint io_mem,
|
||
- uint read_write,
|
||
- uint buff_mode)
|
||
-{
|
||
- u32 cam_cmd_reg = 0;
|
||
-
|
||
- cam_cmd_reg |= (addr_size << CAM_CMD_ADDR_SIZE_OFFS);
|
||
- cam_cmd_reg |= (wr_data << CAM_CMD_WR_DATA_OFFS);
|
||
- cam_cmd_reg |= (io_mem << CAM_CMD_IO_MEM_OFFS);
|
||
- cam_cmd_reg |= (read_write << CAM_CMD_RD_WR_OFFS);
|
||
- cam_cmd_reg |= (buff_mode << CAM_CMD_BUFF_MODE_OFFS);
|
||
- writel_relaxed(cam_cmd_reg, tsc_device->base + TSC_CAM_CMD);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_data_transaction() - Blocking function that manage the data transactions.
|
||
- *
|
||
- * @tsc_ci: TSC CI device.
|
||
- * @io_mem: The value for the io_mem register field- 1 for IO transaction,
|
||
- * 0 for memory transaction.
|
||
- * @read_write: The value for the read_write register field- 1 for read
|
||
- * transaction, 0 for write transaction.
|
||
- * @buff_mode: The value for the buff_mode register field- 1 for buffer mode,
|
||
- * 0 for single byte mode.
|
||
- * @arg: The argument received from the user-space via a data transaction
|
||
- * IOCTL. It is from one of the two following types:
|
||
- * "struct tsc_single_byte_mode" and "struct tsc_buffer_mode".
|
||
- *
|
||
- * Receiving the transaction paramters from the user-space. Configure the HW
|
||
- * registers to initiate a data transaction with the cam. Wait for an
|
||
- * interrupt indicating the transaction is over and return the the data read
|
||
- * from the cam in case of single-byte read transaction.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_data_transaction(struct tsc_ci_chdev *tsc_ci, uint io_mem,
|
||
- uint read_write, uint buff_mode, unsigned long arg)
|
||
-{
|
||
- struct tsc_single_byte_mode arg_byte;
|
||
- struct tsc_buffer_mode arg_buff;
|
||
- u16 addr_size;
|
||
- u8 wr_data;
|
||
- uint timeout;
|
||
- u32 cam_cmd_reg;
|
||
- struct ion_handle *ion_handle = NULL;
|
||
- ion_phys_addr_t iova = 0;
|
||
- unsigned long buffer_size = 0;
|
||
- unsigned long flags = 0;
|
||
- int ret = 0;
|
||
-
|
||
- if (!arg)
|
||
- return -EINVAL;
|
||
-
|
||
- /* make sure the tsc is in idle state before configuring the cam */
|
||
- if (!is_tsc_idle(tsc_ci)) {
|
||
- ret = -EBUSY;
|
||
- goto finish;
|
||
- }
|
||
-
|
||
- INIT_COMPLETION(tsc_ci->transaction_finish);
|
||
-
|
||
- /* copying data from the ioctl parameter */
|
||
- if (buff_mode == SINGLE_BYTE_MODE) {
|
||
- if (copy_from_user(&arg_byte, (void *)arg,
|
||
- sizeof(struct tsc_single_byte_mode))) {
|
||
- ret = -EFAULT;
|
||
- goto err_copy_arg;
|
||
- }
|
||
- addr_size = arg_byte.address;
|
||
- if (IO_TRANSACTION == io_mem &&
|
||
- addr_size > CICAM_MAX_ADDRESS) {
|
||
- pr_err("%s: wrong address parameter: %d\n", __func__,
|
||
- addr_size);
|
||
- ret = -EFAULT;
|
||
- goto err_copy_arg;
|
||
- }
|
||
- wr_data = arg_byte.data;
|
||
- timeout = arg_byte.timeout;
|
||
- } else {
|
||
- if (copy_from_user(&arg_buff, (void *)arg,
|
||
- sizeof(struct tsc_buffer_mode))) {
|
||
- ret = -EFAULT;
|
||
- goto err_copy_arg;
|
||
- }
|
||
- addr_size = arg_buff.buffer_size;
|
||
- if (!addr_size) {
|
||
- pr_err("%s: size parameter is 0\n", __func__);
|
||
- ret = -EFAULT;
|
||
- goto err_copy_arg;
|
||
- }
|
||
- wr_data = 0;
|
||
- timeout = arg_buff.timeout;
|
||
-
|
||
- /* import ion handle from the ion fd passed from user-space */
|
||
- ion_handle = ion_import_dma_buf
|
||
- (tsc_device->iommu_info.ion_client, arg_buff.buffer_fd);
|
||
- if (IS_ERR_OR_NULL(ion_handle)) {
|
||
- pr_err("%s: get_ION_handle failed\n", __func__);
|
||
- ret = -EIO;
|
||
- goto err_ion_handle;
|
||
- }
|
||
-
|
||
- /*
|
||
- * mapping the ion handle to the VBIF and get the virtual
|
||
- * address
|
||
- */
|
||
- ret = ion_map_iommu(tsc_device->iommu_info.ion_client,
|
||
- ion_handle, tsc_device->iommu_info.domain_num,
|
||
- tsc_device->iommu_info.partition_num, SZ_4K,
|
||
- 0, &iova, &buffer_size, 0, 0);
|
||
-
|
||
- if (ret != 0) {
|
||
- pr_err("%s: get_ION_kernel physical addr fail\n",
|
||
- __func__);
|
||
- goto err_ion_map;
|
||
- }
|
||
-
|
||
- /*
|
||
- * writing the buffer virtual address to the register for buffer
|
||
- * address of buffer mode
|
||
- */
|
||
- if (read_write == READ_TRANSACTION)
|
||
- writel_relaxed(iova,
|
||
- tsc_device->base + TSC_RD_BUFF_ADDR);
|
||
- else /* write transaction */
|
||
- writel_relaxed(iova,
|
||
- tsc_device->base + TSC_WR_BUFF_ADDR);
|
||
- }
|
||
-
|
||
- /* configuring the cam command register */
|
||
- tsc_config_cam_data_transaction(addr_size, wr_data, io_mem, read_write,
|
||
- buff_mode);
|
||
-
|
||
- /*
|
||
- * This function assume the mutex is locked before calling the function,
|
||
- * so mutex has to be unlocked before going to sleep when waiting for
|
||
- * the transaction.
|
||
- */
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
- /* waiting for EOT interrupt or timeout */
|
||
- if (!wait_for_completion_timeout(&tsc_ci->transaction_complete,
|
||
- msecs_to_jiffies(timeout))) {
|
||
- pr_err("%s: Error: wait for transaction timed-out\n", __func__);
|
||
- ret = -ETIMEDOUT;
|
||
- mutex_lock(&tsc_ci->mutex);
|
||
- /* Aborting the transaction if it's buffer mode */
|
||
- if (buff_mode) {
|
||
- cam_cmd_reg = readl_relaxed(tsc_device->base +
|
||
- TSC_CAM_CMD);
|
||
- SET_BIT(CAM_CMD_ABORT, cam_cmd_reg);
|
||
- writel_relaxed(cam_cmd_reg, tsc_device->base +
|
||
- TSC_CAM_CMD);
|
||
- }
|
||
- goto finish;
|
||
- }
|
||
- mutex_lock(&tsc_ci->mutex);
|
||
-
|
||
- /* Checking if transaction ended with error */
|
||
- spin_lock_irqsave(&tsc_ci->spinlock, flags);
|
||
- if (tsc_ci->transaction_state == TRANSACTION_ERROR) {
|
||
- tsc_ci->transaction_state = BEFORE_TRANSACTION;
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
- pr_err("%s: Transaction error\n", __func__);
|
||
- ret = -EBADE; /* Invalid exchange error code */
|
||
- goto finish;
|
||
- } else if (tsc_ci->transaction_state == TRANSACTION_CARD_REMOVED) {
|
||
- tsc_ci->transaction_state = BEFORE_TRANSACTION;
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
- pr_err("%s: Card was removed during the transaction. Aborting\n",
|
||
- __func__);
|
||
- ret = -ECONNABORTED;
|
||
- /* Aborting the transaction if it's buffer mode */
|
||
- if (buff_mode) {
|
||
- cam_cmd_reg = readl_relaxed(tsc_device->base +
|
||
- TSC_CAM_CMD);
|
||
- SET_BIT(CAM_CMD_ABORT, cam_cmd_reg);
|
||
- writel_relaxed(cam_cmd_reg, tsc_device->base +
|
||
- TSC_CAM_CMD);
|
||
- }
|
||
- goto finish;
|
||
- }
|
||
-
|
||
- /* reseting the argument after reading the interrupt type */
|
||
- tsc_ci->transaction_state = BEFORE_TRANSACTION;
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
-
|
||
- /*
|
||
- * Only on case of read single byte operation, we need to copy the data
|
||
- * to the arg data field
|
||
- */
|
||
- if (buff_mode == SINGLE_BYTE_MODE && read_write == READ_TRANSACTION)
|
||
- ret = put_user(readl_relaxed(tsc_device->base +
|
||
- TSC_CAM_RD_DATA),
|
||
- &((struct tsc_single_byte_mode *)arg)->data);
|
||
-
|
||
-finish:
|
||
- if (iova != 0)
|
||
- ion_unmap_iommu(tsc_device->iommu_info.ion_client, ion_handle,
|
||
- tsc_device->iommu_info.domain_num,
|
||
- tsc_device->iommu_info.partition_num);
|
||
-err_ion_map:
|
||
- if (!IS_ERR_OR_NULL(ion_handle))
|
||
- ion_free(tsc_device->iommu_info.ion_client, ion_handle);
|
||
-err_ion_handle:
|
||
-err_copy_arg:
|
||
- tsc_ci->data_busy = false;
|
||
- INIT_COMPLETION(tsc_ci->transaction_complete);
|
||
- complete_all(&tsc_ci->transaction_finish);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_personality_change() - change the PCMCIA pins state.
|
||
- *
|
||
- * @pcmcia_state: The new state of the PCMCIA pins.
|
||
- *
|
||
- * Configure the TLMM pins of the PCMCIA according to received state and
|
||
- * the current pinctrl configuration of the other pins. This function assums the
|
||
- * PCMCIA pinctrl definitions were successfully parsed from the devicetree (this
|
||
- * check is done at open device).
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_personality_change(enum tsc_cam_personality pcmcia_state)
|
||
-{
|
||
- int ret = 0;
|
||
- struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
|
||
- struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
|
||
- u32 reg = 0;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (pcmcia_state == (enum tsc_cam_personality)pcurr_state->pcmcia_state)
|
||
- goto exit;
|
||
-
|
||
- /* Transition from current pinctrl state to curr + new pcmcia state */
|
||
- switch (pcmcia_state) {
|
||
- case TSC_CICAM_PERSONALITY_CI:
|
||
- if (pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_card);
|
||
- else if (pcurr_state->ts0 && !pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_card);
|
||
- else if (!pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_card);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_card);
|
||
- break;
|
||
- case TSC_CICAM_PERSONALITY_CIPLUS:
|
||
- if (pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts_ci_plus);
|
||
- else if (pcurr_state->ts0 && !pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0_ci_plus);
|
||
- else if (!pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1_ci_plus);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ci_plus);
|
||
- break;
|
||
- case TSC_CICAM_PERSONALITY_DISABLE:
|
||
- if (pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->dual_ts);
|
||
- else if (pcurr_state->ts0 && !pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts0);
|
||
- else if (!pcurr_state->ts0 && pcurr_state->ts1)
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->ts1);
|
||
- else
|
||
- ret = pinctrl_select_state(ppinctrl->pinctrl,
|
||
- ppinctrl->disable);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Wrong personality parameter\n", __func__);
|
||
- ret = -EINVAL;
|
||
- goto exit;
|
||
- }
|
||
-
|
||
- if (ret != 0) {
|
||
- pr_err("%s: error changing PCMCIA pins. ret value = %d\n",
|
||
- __func__, ret);
|
||
- ret = -EINVAL;
|
||
- goto exit;
|
||
- }
|
||
-
|
||
- /* Update the current pcmcia state in the internal struct */
|
||
- pcurr_state->pcmcia_state = (enum pcmcia_state)pcmcia_state;
|
||
-
|
||
- /*
|
||
- * Setting CAM TSIF OE to enable I/O transactions for CI/+ cards
|
||
- * or clearing it when moving to disable state
|
||
- */
|
||
- if (TSC_CICAM_PERSONALITY_CI == pcmcia_state ||
|
||
- TSC_CICAM_PERSONALITY_CIPLUS == pcmcia_state) {
|
||
- SET_BIT(TSC_CICAM_TSIF_OE_OFFS, reg);
|
||
- writel_relaxed(reg, tsc_device->base + TSC_CICAM_TSIF);
|
||
- } else {
|
||
- CLEAR_BIT(TSC_CICAM_TSIF_OE_OFFS, reg);
|
||
- writel_relaxed(reg, tsc_device->base + TSC_CICAM_TSIF);
|
||
- }
|
||
-
|
||
-exit:
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_reset_cam() - HW reset to the CAM.
|
||
- *
|
||
- * Toggle the reset pin of the pcmcia to make a HW reset.
|
||
- * This function assumes that pinctrl_select_state was already called on the
|
||
- * reset pin with its active state (happens during personality change).
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_reset_cam(void)
|
||
-{
|
||
- int ret;
|
||
- int reset_gpio = tsc_device->reset_cam_gpio;
|
||
-
|
||
- /* Toggle the GPIO to create a reset pulse */
|
||
- ret = gpio_direction_output(reset_gpio, 0); /* Make sure it's 0 */
|
||
- if (ret != 0)
|
||
- goto err;
|
||
-
|
||
- ret = gpio_direction_output(reset_gpio, 1); /* Assert */
|
||
- if (ret != 0)
|
||
- goto err;
|
||
-
|
||
- /*
|
||
- * Waiting to enable the CAM to process the assertion before the
|
||
- * deassertion. 1ms is needed for this processing.
|
||
- */
|
||
- usleep(1000);
|
||
-
|
||
- ret = gpio_direction_output(reset_gpio, 0); /* Deassert */
|
||
- if (ret != 0)
|
||
- goto err;
|
||
-
|
||
- return 0;
|
||
-err:
|
||
- pr_err("%s: Failed writing to reset cam GPIO\n", __func__);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_reset_registers() - Reset the TSC registers.
|
||
- *
|
||
- * Write specific reset values to the TSC registers, managed by the driver.
|
||
- */
|
||
-static void tsc_reset_registers(void)
|
||
-{
|
||
- /* Reset state - all mux transfer ext. demod 0 */
|
||
- writel_relaxed(0x00000000, tsc_device->base + TSC_MUX_CFG);
|
||
-
|
||
- /* Disabling TSIFs inputs, putting polarity to normal, data as serial */
|
||
- writel_relaxed(0x02000200, tsc_device->base + TSC_IN_IFC_EXT);
|
||
- writel_relaxed(0x02000200, tsc_device->base + TSC_IN_IFC_CFG_INT);
|
||
-
|
||
- /* Reseting TSC_FSM_STATE_MASK to represent all the states but poll */
|
||
- writel_relaxed(0x3333300F, tsc_device->base + TSC_FSM_STATE_MASK);
|
||
-
|
||
- /* Clearing all the CAM interrupt */
|
||
- writel_relaxed(0x1F, tsc_device->base + TSC_IRQ_CLR);
|
||
-
|
||
- /* Disabling all cam interrupts (enable is done at - open) */
|
||
- writel_relaxed(0x00, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- /* Disabling HW polling */
|
||
- writel_relaxed(0x00, tsc_device->base + TSC_CIP_CFG);
|
||
-
|
||
- /* Reset state - address for read/write buffer */
|
||
- writel_relaxed(0x00000000, tsc_device->base + TSC_RD_BUFF_ADDR);
|
||
- writel_relaxed(0x00000000, tsc_device->base + TSC_WR_BUFF_ADDR);
|
||
-
|
||
- /* Clearing false cd counter */
|
||
- writel_relaxed(0x01, tsc_device->base + TSC_FALSE_CD_CLR);
|
||
- writel_relaxed(0x00, tsc_device->base + TSC_FALSE_CD_CLR);
|
||
-
|
||
- /* Disabling TSIF out to cicam and IO read/write with the CAM */
|
||
- writel_relaxed(0x00000000, tsc_device->base + TSC_CICAM_TSIF);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_disable_tsifs() - Disable all the TSC Tsifs.
|
||
- *
|
||
- * Disable the TSIFs of the ext. demods, the int. demod and the cam on both
|
||
- * directions.
|
||
- */
|
||
-static void tsc_disable_tsifs(void)
|
||
-{
|
||
- u32 reg;
|
||
-
|
||
- /* Ext. TSIFs */
|
||
- reg = readl_relaxed(tsc_device->base + TSC_IN_IFC_EXT);
|
||
- SET_BIT(TSIF_DISABLE_OFFS, reg);
|
||
- SET_BIT((TSIF_DISABLE_OFFS + 16), reg);
|
||
- writel_relaxed(reg, tsc_device->base + TSC_IN_IFC_EXT);
|
||
-
|
||
- /* Int. TSIF and TSIF-in from the CAM */
|
||
- reg = readl_relaxed(tsc_device->base + TSC_IN_IFC_CFG_INT);
|
||
- SET_BIT(TSIF_DISABLE_OFFS, reg);
|
||
- SET_BIT((TSIF_DISABLE_OFFS + 16), reg);
|
||
- writel_relaxed(reg, tsc_device->base + TSC_IN_IFC_CFG_INT);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_power_on_clocks() - power-on the TSC clocks.
|
||
- *
|
||
- * Power-on the TSC clocks required for Mux and/or CI operations.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_power_on_clocks(void)
|
||
-{
|
||
- int ret = 0;
|
||
- unsigned long rate_in_hz = 0;
|
||
-
|
||
- /* Enabling the clocks */
|
||
- ret = clk_prepare_enable(tsc_device->ahb_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tsc_ahb_clk", __func__);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- /* We need to set the rate of ci clock before enabling it */
|
||
- rate_in_hz = clk_round_rate(tsc_device->ci_clk, 1);
|
||
- if (clk_set_rate(tsc_device->ci_clk, rate_in_hz)) {
|
||
- pr_err("%s: Failed to set rate to tsc_ci clock\n", __func__);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- ret = clk_prepare_enable(tsc_device->ci_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tsc_ci_clk", __func__);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-err:
|
||
- clk_disable_unprepare(tsc_device->ahb_clk);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_power_off_clocks() - power-off the TSC clocks.
|
||
- *
|
||
- * Power-off the TSC clocks required for Mux and/or CI operations.
|
||
- */
|
||
-static void tsc_power_off_clocks(void)
|
||
-{
|
||
- clk_disable_unprepare(tsc_device->ahb_clk);
|
||
- clk_disable_unprepare(tsc_device->ci_clk);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_mux_power_on_clocks() - power-on the TSC Mux clocks.
|
||
- *
|
||
- * Power-on the TSC clocks required only for Mux operations, and not for CI.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_mux_power_on_clocks(void)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- /* Setting the cicam clock rate */
|
||
- ret = clk_set_rate(tsc_device->cicam_ts_clk, CICAM_CLK_RATE_7MHZ);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't set rate for tsc_cicam_ts_clk", __func__);
|
||
- goto err_set_rate;
|
||
- }
|
||
-
|
||
- /* Setting the TSC serial clock rate */
|
||
- ret = clk_set_rate(tsc_device->ser_clk, TSC_SER_CLK_RATE);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't set rate for tsc serial clock", __func__);
|
||
- goto err_set_rate;
|
||
- }
|
||
-
|
||
- /* Setting the TSC parallel clock rate */
|
||
- ret = clk_set_rate(tsc_device->par_clk, TSC_PAR_CLK_RATE);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't set rate for tsc parallel clock", __func__);
|
||
- goto err_set_rate;
|
||
- }
|
||
-
|
||
- /* Enabling the clocks */
|
||
- ret = clk_prepare_enable(tsc_device->ser_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tsc_ser_clk", __func__);
|
||
- goto err_ser_clk;
|
||
- }
|
||
- ret = clk_prepare_enable(tsc_device->par_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tsc_par_clk", __func__);
|
||
- goto err_par_clk;
|
||
- }
|
||
- ret = clk_prepare_enable(tsc_device->cicam_ts_clk);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Can't start tsc_cicam_ts_clk", __func__);
|
||
- goto err_cicam_ts_clk;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_cicam_ts_clk:
|
||
- clk_disable_unprepare(tsc_device->par_clk);
|
||
-err_par_clk:
|
||
- clk_disable_unprepare(tsc_device->ser_clk);
|
||
-err_ser_clk:
|
||
-err_set_rate:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_mux_power_off_clocks() - power-off the TSC Mux clocks.
|
||
- *
|
||
- * Power-off the TSC clocks required only for Mux operations, and not for CI.
|
||
- */
|
||
-static void tsc_mux_power_off_clocks(void)
|
||
-{
|
||
- clk_disable_unprepare(tsc_device->ser_clk);
|
||
- clk_disable_unprepare(tsc_device->par_clk);
|
||
- clk_disable_unprepare(tsc_device->cicam_ts_clk);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_device_power_up() - Power init done by the first device opened.
|
||
- *
|
||
- * Check if it's the first device and enable the GDSC,power-on the TSC clocks
|
||
- * required for both Mux and CI, Vote for the bus and reset the registers to a
|
||
- * known default values.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_device_power_up(void)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (tsc_device->num_device_open > 0)
|
||
- goto not_first_device;
|
||
-
|
||
- /* Enable the GDSC */
|
||
- ret = regulator_enable(tsc_device->gdsc);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Failed to enable regulator\n", __func__);
|
||
- goto err_regulator;
|
||
- }
|
||
-
|
||
- /* Power-on the clocks needed by Mux and CI */
|
||
- ret = tsc_power_on_clocks();
|
||
- if (ret != 0)
|
||
- goto err_power_clocks;
|
||
-
|
||
- /* Voting for bus bandwidth */
|
||
- if (tsc_device->bus_client) {
|
||
- ret = msm_bus_scale_client_update_request
|
||
- (tsc_device->bus_client, 1);
|
||
- if (ret) {
|
||
- pr_err("%s: Can't enable bus\n", __func__);
|
||
- goto err_bus;
|
||
- }
|
||
- }
|
||
-
|
||
- /* Reset the TSC TLMM pins to a default state */
|
||
- ret = pinctrl_select_state(tsc_device->pinctrl_info.pinctrl,
|
||
- tsc_device->pinctrl_info.disable);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Failed to disable the TLMM pins\n", __func__);
|
||
- goto err_pinctrl;
|
||
- }
|
||
- /* Update the current pinctrl state in the internal struct */
|
||
- tsc_device->pinctrl_info.curr_state.ts0 = false;
|
||
- tsc_device->pinctrl_info.curr_state.ts1 = false;
|
||
- tsc_device->pinctrl_info.curr_state.pcmcia_state =
|
||
- TSC_CICAM_PERSONALITY_DISABLE;
|
||
-
|
||
- /* Reset TSC registers to a default known state */
|
||
- tsc_reset_registers();
|
||
-
|
||
-not_first_device:
|
||
- tsc_device->num_device_open++;
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return ret;
|
||
-
|
||
-err_pinctrl:
|
||
- if (tsc_device->bus_client)
|
||
- msm_bus_scale_client_update_request(tsc_device->bus_client, 0);
|
||
-err_bus:
|
||
- tsc_power_off_clocks();
|
||
-err_power_clocks:
|
||
- regulator_disable(tsc_device->gdsc);
|
||
-err_regulator:
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_device_power_off() - Power off done by the last device closed.
|
||
- *
|
||
- * Check if it's the last device and unvote the bus, power-off the TSC clocks
|
||
- * required for both Mux and CI, disable the TLMM pins and disable the GDSC.
|
||
- */
|
||
-static void tsc_device_power_off(void)
|
||
-{
|
||
- mutex_lock(&tsc_device->mutex);
|
||
-
|
||
- if (tsc_device->num_device_open > 1)
|
||
- goto not_last_device;
|
||
-
|
||
- pinctrl_select_state(tsc_device->pinctrl_info.pinctrl,
|
||
- tsc_device->pinctrl_info.disable);
|
||
- if (tsc_device->bus_client)
|
||
- msm_bus_scale_client_update_request(tsc_device->bus_client, 0);
|
||
-
|
||
- tsc_power_off_clocks();
|
||
- regulator_disable(tsc_device->gdsc);
|
||
-
|
||
-not_last_device:
|
||
- tsc_device->num_device_open--;
|
||
- mutex_unlock(&tsc_device->mutex);
|
||
-}
|
||
-
|
||
-
|
||
-/************************** TSC file operations **************************/
|
||
-/**
|
||
- * tsc_mux_open() - init the TSC Mux char device.
|
||
- *
|
||
- * @inode: The inode associated with the TSC Mux device.
|
||
- * @flip: The file pointer associated with the TSC Mux device.
|
||
- *
|
||
- * Enables only one open Mux device.
|
||
- * Init all the data structures and vote for all the power resources needed.
|
||
- * Manage reference counters for initiating resources upon first open.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_mux_open(struct inode *inode, struct file *filp)
|
||
-{
|
||
- struct tsc_mux_chdev *tsc_mux;
|
||
- int ret = 0;
|
||
- u32 ena_reg;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->mux_chdev.mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (tsc_device->num_mux_opened > 0) {
|
||
- pr_err("%s: Too many devices open\n", __func__);
|
||
- mutex_unlock(&tsc_device->mux_chdev.mutex);
|
||
- return -EMFILE;
|
||
- }
|
||
- tsc_device->num_mux_opened++;
|
||
-
|
||
- tsc_mux = container_of(inode->i_cdev, struct tsc_mux_chdev, cdev);
|
||
- filp->private_data = tsc_mux;
|
||
-
|
||
- /* Init all resources if it's the first device (checked inside) */
|
||
- ret = tsc_device_power_up();
|
||
- if (ret != 0)
|
||
- goto err_first_device;
|
||
-
|
||
- /* Power-on the Mux clocks */
|
||
- ret = tsc_mux_power_on_clocks();
|
||
- if (ret != 0)
|
||
- goto err_mux_clocks;
|
||
-
|
||
- /* Init TSC Mux args */
|
||
- spin_lock_init(&tsc_mux->spinlock);
|
||
- init_waitqueue_head(&tsc_mux->poll_queue);
|
||
- tsc_mux->rate_interrupt = false;
|
||
-
|
||
- /* Enabling TSC Mux cam interrupt of rate mismatch */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- SET_BIT(CAM_IRQ_RATE_MISMATCH_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- mutex_unlock(&tsc_device->mux_chdev.mutex);
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_mux_clocks:
|
||
- /* De-init all resources if it's the only device (checked inside) */
|
||
- tsc_device_power_off();
|
||
-err_first_device:
|
||
- tsc_device->num_mux_opened--;
|
||
- mutex_unlock(&tsc_device->mux_chdev.mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_ci_open() - init the TSC CI char device.
|
||
- *
|
||
- * @inode: The inode associated with the TSC Mux device.
|
||
- * @flip: The file pointer associated with the TSC Mux device.
|
||
- *
|
||
- * Enables only one open CI device.
|
||
- * Init all the data structures and vote for all the power resources needed.
|
||
- * Manage reference counters for initiating resources upon first open.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_ci_open(struct inode *inode, struct file *filp)
|
||
-{
|
||
- struct tsc_ci_chdev *tsc_ci;
|
||
- int ret = 0;
|
||
- u32 ena_reg;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_device->ci_chdev.mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (tsc_device->num_ci_opened > 0) {
|
||
- pr_err("%s: Too many devices open\n", __func__);
|
||
- mutex_unlock(&tsc_device->ci_chdev.mutex);
|
||
- return -EMFILE;
|
||
- }
|
||
-
|
||
- if (!tsc_device->pinctrl_info.is_pcmcia) {
|
||
- pr_err("%s: No pcmcia pinctrl definitions were found in the TSC devicetree\n",
|
||
- __func__);
|
||
- mutex_unlock(&tsc_device->ci_chdev.mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- tsc_device->num_ci_opened++;
|
||
-
|
||
- tsc_ci = container_of(inode->i_cdev, struct tsc_ci_chdev, cdev);
|
||
- filp->private_data = tsc_ci;
|
||
-
|
||
- /* Init all resources if it's the first device (checked inside) */
|
||
- ret = tsc_device_power_up();
|
||
- if (ret != 0)
|
||
- goto err_first_device;
|
||
-
|
||
- /* powering-up the tspp2 and VBIF clocks */
|
||
- ret = tsc_power_on_buff_mode_clocks();
|
||
- if (ret != 0)
|
||
- goto err_buff_clocks;
|
||
-
|
||
- /* Request reset CAM GPIO */
|
||
- ret = gpio_request(tsc_device->reset_cam_gpio, "tsc_ci_reset");
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Failed to request reset CAM GPIO\n", __func__);
|
||
- goto err_gpio_req;
|
||
- }
|
||
-
|
||
- /* Set the reset line to default "no card" state */
|
||
- ret = gpio_direction_output(tsc_device->reset_cam_gpio, 1);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: Failed to assert the reset CAM GPIO\n", __func__);
|
||
- goto err_assert;
|
||
- }
|
||
-
|
||
- /* Attach the iommu group to support the required memory mapping */
|
||
- if (!tsc_iommu_bypass) {
|
||
- ret = iommu_attach_group(tsc_device->iommu_info.domain,
|
||
- tsc_device->iommu_info.group);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: iommu_attach_group failed\n", __func__);
|
||
- goto err_iommu_attach;
|
||
- }
|
||
- }
|
||
-
|
||
- /* Init TSC CI args */
|
||
- spin_lock_init(&tsc_ci->spinlock);
|
||
- init_waitqueue_head(&tsc_ci->poll_queue);
|
||
- tsc_ci->transaction_state = BEFORE_TRANSACTION;
|
||
- tsc_ci->data_busy = false;
|
||
- tsc_device->card_power = false;
|
||
-
|
||
- /*
|
||
- * Init hw card status flag according to the pins' state.
|
||
- * No need to protect from interrupt because the handler is not
|
||
- * registred yet.
|
||
- */
|
||
- tsc_update_hw_card_status();
|
||
- tsc_ci->card_status = tsc_device->hw_card_status;
|
||
-
|
||
- /* If a card is already inserted - need to power up the card */
|
||
- if (tsc_device->hw_card_status == TSC_CARD_STATUS_DETECTED) {
|
||
- ret = tsc_card_power_up();
|
||
- if (ret != 0)
|
||
- pr_err("%s: card power-up failed\n", __func__);
|
||
- else
|
||
- tsc_device->card_power = true;
|
||
- }
|
||
-
|
||
- /* Enabling the TSC CI cam interrupts: EOT and Err */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- SET_BIT(CAM_IRQ_EOT_OFFS, ena_reg);
|
||
- SET_BIT(CAM_IRQ_ERR_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- /* Registering the CAM cmd interrupt handler */
|
||
- ret = request_irq(tsc_device->cam_cmd_irq, tsc_cam_cmd_irq_handler,
|
||
- IRQF_SHARED, dev_name(&tsc_device->pdev->dev),
|
||
- tsc_device);
|
||
- if (ret) {
|
||
- pr_err("%s: failed to request TSC IRQ %d : %d",
|
||
- __func__, tsc_device->cam_cmd_irq, ret);
|
||
- goto err_cam_irq;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Registering the card detect interrupt handler (this interrupt is
|
||
- * enabled by default, right after this registration)
|
||
- */
|
||
- ret = request_threaded_irq(tsc_device->card_detection_irq,
|
||
- NULL, tsc_card_detect_irq_thread_handler,
|
||
- IRQF_ONESHOT | IRQF_TRIGGER_RISING,
|
||
- dev_name(&tsc_device->pdev->dev), tsc_device);
|
||
- if (ret) {
|
||
- pr_err("%s: failed to request TSC IRQ %d : %d",
|
||
- __func__, tsc_device->card_detection_irq, ret);
|
||
- goto err_card_irq;
|
||
- }
|
||
-
|
||
- mutex_unlock(&tsc_device->ci_chdev.mutex);
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_card_irq:
|
||
- free_irq(tsc_device->cam_cmd_irq, tsc_device);
|
||
-err_cam_irq:
|
||
- if (!tsc_iommu_bypass)
|
||
- iommu_detach_group(tsc_device->iommu_info.domain,
|
||
- tsc_device->iommu_info.group);
|
||
-err_iommu_attach:
|
||
- gpio_free(tsc_device->reset_cam_gpio);
|
||
-err_assert:
|
||
-err_gpio_req:
|
||
- tsc_power_off_buff_mode_clocks();
|
||
-err_buff_clocks:
|
||
- /* De-init all resources if it's the only device (checked inside) */
|
||
- tsc_device_power_off();
|
||
-err_first_device:
|
||
- tsc_device->num_ci_opened--;
|
||
- mutex_unlock(&tsc_device->ci_chdev.mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_mux_release() - Release and close the TSC Mux char device.
|
||
- *
|
||
- * @inode: The inode associated with the TSC Mux device.
|
||
- * @flip: The file pointer associated with the TSC Mux device.
|
||
- *
|
||
- * Release all the resources allocated for the Mux device and unvote power
|
||
- * resources.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_mux_release(struct inode *inode, struct file *filp)
|
||
-{
|
||
- struct tsc_mux_chdev *tsc_mux;
|
||
- u32 ena_reg;
|
||
-
|
||
- tsc_mux = filp->private_data;
|
||
- if (!tsc_mux)
|
||
- return -EINVAL;
|
||
-
|
||
- mutex_lock(&tsc_mux->mutex);
|
||
-
|
||
- tsc_mux_power_off_clocks();
|
||
-
|
||
- /* Disable the TSIFs */
|
||
- tsc_disable_tsifs();
|
||
- /* Disabling rate mismatch interrupt */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- CLEAR_BIT(CAM_IRQ_RATE_MISMATCH_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- tsc_device_power_off();
|
||
-
|
||
- tsc_device->num_mux_opened--;
|
||
- mutex_unlock(&tsc_mux->mutex);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_ci_release() - Release and close the TSC CI char device.
|
||
- *
|
||
- * @inode: The inode associated with the TSC CI device.
|
||
- * @flip: The file pointer associated with the TSC CI device.
|
||
- *
|
||
- * Release all the resources allocated for the CI device and unvote power
|
||
- * resources.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_ci_release(struct inode *inode, struct file *filp)
|
||
-{
|
||
- struct tsc_ci_chdev *tsc_ci;
|
||
- u32 ena_reg;
|
||
- int ret;
|
||
-
|
||
- tsc_ci = filp->private_data;
|
||
- if (!tsc_ci)
|
||
- return -EINVAL;
|
||
-
|
||
- mutex_lock(&tsc_ci->mutex);
|
||
-
|
||
- /* If in the middle of a data transaction- wake-up completion */
|
||
- if (tsc_ci->data_busy) {
|
||
- /* Closing the device is similar in behavior to card removal */
|
||
- tsc_ci->transaction_state = TRANSACTION_CARD_REMOVED;
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
- complete_all(&tsc_ci->transaction_complete);
|
||
- wait_for_completion(&tsc_ci->transaction_finish);
|
||
- mutex_lock(&tsc_ci->mutex);
|
||
- }
|
||
-
|
||
- /* clearing EOT and ERR interrupts */
|
||
- ena_reg = readl_relaxed(tsc_device->base + TSC_IRQ_ENA);
|
||
- CLEAR_BIT(CAM_IRQ_EOT_OFFS, ena_reg);
|
||
- CLEAR_BIT(CAM_IRQ_ERR_OFFS, ena_reg);
|
||
- writel_relaxed(ena_reg, tsc_device->base + TSC_IRQ_ENA);
|
||
-
|
||
- /* Cancel the interrupt handlers registration */
|
||
- free_irq(tsc_device->card_detection_irq, tsc_device);
|
||
- free_irq(tsc_device->cam_cmd_irq, tsc_device);
|
||
-
|
||
- /* power down the card interface if it's currently powered up */
|
||
- if (tsc_device->hw_card_status == TSC_CARD_STATUS_DETECTED &&
|
||
- tsc_device->card_power) {
|
||
- ret = tsc_card_power_down();
|
||
- if (ret != 0)
|
||
- pr_err("%s: card power-down failed\n", __func__);
|
||
- }
|
||
-
|
||
- if (!tsc_iommu_bypass)
|
||
- iommu_detach_group(tsc_device->iommu_info.domain,
|
||
- tsc_device->iommu_info.group);
|
||
-
|
||
- gpio_free(tsc_device->reset_cam_gpio);
|
||
-
|
||
- tsc_power_off_buff_mode_clocks();
|
||
- tsc_device_power_off();
|
||
-
|
||
- tsc_device->num_ci_opened--;
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_mux_poll() - Perform polling on a designated wait-queue.
|
||
- *
|
||
- * @flip: The file pointer associated with the TSC Mux device.
|
||
- * @p: The poll-table struct of the kernel.
|
||
- *
|
||
- * Add the TSC Mux wait-queue to the poll-table. Poll until a rate mismatch
|
||
- * interrupt is received.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static unsigned int tsc_mux_poll(struct file *filp, struct poll_table_struct *p)
|
||
-{
|
||
- unsigned long flags;
|
||
- unsigned int mask = 0;
|
||
- struct tsc_mux_chdev *tsc_mux;
|
||
-
|
||
- tsc_mux = filp->private_data;
|
||
- if (!tsc_mux)
|
||
- return -EINVAL;
|
||
-
|
||
- /* register the wait queue for rate mismatch interrupt */
|
||
- poll_wait(filp, &tsc_mux->poll_queue, p);
|
||
-
|
||
- /* Setting the mask upon rate mismatch irq and clearing the flag */
|
||
- spin_lock_irqsave(&tsc_mux->spinlock, flags);
|
||
- if (tsc_mux->rate_interrupt) {
|
||
- mask = POLLPRI;
|
||
- tsc_mux->rate_interrupt = false;
|
||
- }
|
||
- spin_unlock_irqrestore(&tsc_mux->spinlock, flags);
|
||
-
|
||
- return mask;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_ci_poll() - Perform polling on a designated wait-queue.
|
||
- *
|
||
- * @flip: The file pointer associated with the TSC CI device.
|
||
- * @p: The poll-table struct of the kernel.
|
||
- *
|
||
- * Add the TSC Mux wait-queue to the poll-table. Poll until a card detection
|
||
- * interrupt is received.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static unsigned int tsc_ci_poll(struct file *filp, struct poll_table_struct *p)
|
||
-{
|
||
- unsigned int mask = 0;
|
||
-
|
||
- struct tsc_ci_chdev *tsc_ci = filp->private_data;
|
||
- if (!tsc_ci)
|
||
- return -EINVAL;
|
||
-
|
||
- /* Register the wait queue for card detection interrupt */
|
||
- poll_wait(filp, &tsc_ci->poll_queue, p);
|
||
-
|
||
- /* Setting the mask upon card detect irq and update ci card state */
|
||
- if (mutex_lock_interruptible(&tsc_ci->mutex))
|
||
- return -ERESTARTSYS;
|
||
- if (tsc_ci->card_status != tsc_device->hw_card_status) {
|
||
- mask = POLLPRI;
|
||
- tsc_ci->card_status = tsc_device->hw_card_status;
|
||
- }
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
-
|
||
- return mask;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_mux_ioctl() - Handle IOCTLs sent from user-space application.
|
||
- *
|
||
- * @flip: The file pointer associated with the TSC Mux device.
|
||
- * @cmd: The IOCTL code sent
|
||
- * @arg: The IOCTL argument (if the IOCTL receives an argument)
|
||
- *
|
||
- * Verify the validity of the IOCTL sent and handle it by updating the
|
||
- * appropriate register or calling a function that handle the IOCTL operation.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static long tsc_mux_ioctl(struct file *filp,
|
||
- unsigned int cmd,
|
||
- unsigned long arg)
|
||
-{
|
||
- int ret = 0;
|
||
- struct tsc_mux_chdev *tsc_mux;
|
||
- struct tsc_route tsc_route;
|
||
- struct tsc_tsif_params tsif_params;
|
||
-
|
||
- tsc_mux = filp->private_data;
|
||
- if (!tsc_mux)
|
||
- return -EINVAL;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_mux->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- switch (cmd) {
|
||
- case TSC_CONFIG_ROUTE:
|
||
- if (!arg || copy_from_user(&tsc_route, (void *)arg,
|
||
- sizeof(struct tsc_route))) {
|
||
- ret = -EFAULT;
|
||
- goto err;
|
||
- }
|
||
- ret = tsc_route_mux(tsc_mux, tsc_route.source, tsc_route.dest);
|
||
- break;
|
||
- case TSC_ENABLE_INPUT:
|
||
- ret = tsc_enable_disable_tsif(tsc_mux, arg, TSIF_INPUT_ENABLE);
|
||
- break;
|
||
- case TSC_DISABLE_INPUT:
|
||
- ret = tsc_enable_disable_tsif(tsc_mux, arg, TSIF_INPUT_DISABLE);
|
||
- break;
|
||
- case TSC_SET_TSIF_CONFIG:
|
||
- if (!arg || copy_from_user(&tsif_params, (void *)arg,
|
||
- sizeof(struct tsc_tsif_params))) {
|
||
- ret = -EFAULT;
|
||
- goto err;
|
||
- }
|
||
- ret = tsc_config_tsif(tsc_mux, &tsif_params);
|
||
- break;
|
||
- case TSC_CLEAR_RATE_MISMATCH_IRQ:
|
||
- tsc_enable_rate_irq(tsc_mux);
|
||
- break;
|
||
- case TSC_CICAM_SET_CLOCK:
|
||
- ret = tsc_set_cicam_clk(arg);
|
||
- break;
|
||
- default:
|
||
- ret = -EINVAL;
|
||
- pr_err("%s: Unknown ioctl %i", __func__, cmd);
|
||
- }
|
||
-
|
||
-err:
|
||
- mutex_unlock(&tsc_mux->mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_ci_ioctl() - Handle IOCTLs sent from user-space application.
|
||
- *
|
||
- * @flip: The file pointer associated with the TSC CI device.
|
||
- * @cmd: The IOCTL code sent
|
||
- * @arg: The IOCTL argument (if the IOCTL receives an argument)
|
||
- *
|
||
- * Verify the validity of the IOCTL sent and handle it by updating the
|
||
- * appropriate register or calling a function that handle the IOCTL operation.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static long tsc_ci_ioctl(struct file *filp,
|
||
- unsigned int cmd,
|
||
- unsigned long arg)
|
||
-{
|
||
- int ret = 0;
|
||
- struct tsc_ci_chdev *tsc_ci;
|
||
- unsigned long flags;
|
||
-
|
||
- tsc_ci = filp->private_data;
|
||
- if (!tsc_ci)
|
||
- return -EINVAL;
|
||
-
|
||
- if (mutex_lock_interruptible(&tsc_ci->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- switch (cmd) {
|
||
-
|
||
- case TSC_CAM_RESET:
|
||
- ret = tsc_reset_cam();
|
||
- break;
|
||
- case TSC_CICAM_PERSONALITY_CHANGE:
|
||
- ret = tsc_personality_change(arg);
|
||
- break;
|
||
- case TSC_GET_CARD_STATUS:
|
||
- spin_lock_irqsave(&tsc_ci->spinlock, flags);
|
||
- tsc_ci->card_status = tsc_device->hw_card_status;
|
||
- ret = __put_user(tsc_ci->card_status,
|
||
- (enum tsc_card_status __user *)arg);
|
||
- spin_unlock_irqrestore(&tsc_ci->spinlock, flags);
|
||
- break;
|
||
- case TSC_READ_CAM_MEMORY:
|
||
- ret = tsc_data_transaction(tsc_ci, MEMORY_TRANSACTION,
|
||
- READ_TRANSACTION, SINGLE_BYTE_MODE, arg);
|
||
- break;
|
||
- case TSC_WRITE_CAM_MEMORY:
|
||
- ret = tsc_data_transaction(tsc_ci, MEMORY_TRANSACTION,
|
||
- WRITE_TRANSACTION, SINGLE_BYTE_MODE, arg);
|
||
- break;
|
||
- case TSC_READ_CAM_IO:
|
||
- ret = tsc_data_transaction(tsc_ci, IO_TRANSACTION,
|
||
- READ_TRANSACTION, SINGLE_BYTE_MODE, arg);
|
||
- break;
|
||
- case TSC_WRITE_CAM_IO:
|
||
- ret = tsc_data_transaction(tsc_ci, IO_TRANSACTION,
|
||
- WRITE_TRANSACTION, SINGLE_BYTE_MODE, arg);
|
||
- break;
|
||
- case TSC_READ_CAM_BUFFER:
|
||
- ret = tsc_data_transaction(tsc_ci, IO_TRANSACTION,
|
||
- READ_TRANSACTION, BUFFER_MODE, arg);
|
||
- break;
|
||
- case TSC_WRITE_CAM_BUFFER:
|
||
- ret = tsc_data_transaction(tsc_ci, IO_TRANSACTION,
|
||
- WRITE_TRANSACTION, BUFFER_MODE, arg);
|
||
- break;
|
||
- default:
|
||
- ret = -EINVAL;
|
||
- pr_err("%s: Unknown ioctl %i\n", __func__, cmd);
|
||
- }
|
||
-
|
||
- mutex_unlock(&tsc_ci->mutex);
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/************************** Probe helper-functions **************************/
|
||
-/**
|
||
- * tsc_init_char_driver() - Initialize a character driver.
|
||
- *
|
||
- * @pcdev: A pointer to the cdev structure to initialize.
|
||
- * @pfops: A pointer to the file_operations for this device.
|
||
- * @device_number: A pointer that will store the device number.
|
||
- * @device: A pointer that will store the new device upon success.
|
||
- * @name: A string for the device's name.
|
||
- *
|
||
- * Create a new character device driver inside the TSC class. The new device
|
||
- * is created under "/dev/<name>0".
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_init_char_driver(struct cdev *pcdev,
|
||
- const struct file_operations *pfops,
|
||
- dev_t *pdevice_number,
|
||
- struct device *pdevice,
|
||
- const char *name)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- /* Allocate device number for the char device driver */
|
||
- ret = alloc_chrdev_region(pdevice_number, 0, 1, name);
|
||
- if (ret) {
|
||
- pr_err("%s: alloc_chrdev_region failed: %d\n", name, ret);
|
||
- goto err_devrgn;
|
||
- }
|
||
-
|
||
- /* initializing the char device structures with file operations */
|
||
- cdev_init(pcdev, pfops);
|
||
- pcdev->owner = THIS_MODULE;
|
||
-
|
||
- /* adding the char device structures to the VFS */
|
||
- ret = cdev_add(pcdev, *pdevice_number, 1);
|
||
- if (ret != 0) {
|
||
- pr_err("%s%d: cdev_add failed\n", name, MINOR(*pdevice_number));
|
||
- goto err_cdev_add;
|
||
- }
|
||
-
|
||
- /* create the char devices under "/dev/" and register them to sysfs */
|
||
- pdevice = device_create(tsc_class, NULL, pcdev->dev, NULL, "%s%d", name,
|
||
- MINOR(*pdevice_number));
|
||
- if (IS_ERR(pdevice)) {
|
||
- pr_err("%s%d device_create failed\n", name,
|
||
- MINOR(*pdevice_number));
|
||
- ret = PTR_ERR(pdevice); /* PTR_ERR return -ENOMEM */
|
||
- goto err_device_create;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_device_create:
|
||
- cdev_del(pcdev);
|
||
-err_cdev_add:
|
||
- unregister_chrdev_region(*pdevice_number, 1);
|
||
-err_devrgn:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_get_pinctrl() - Get the TSC pinctrl definitions.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Get the pinctrl states' handles from the device tree. The function doesn't
|
||
- * enforce wrong pinctrl definitions, i.e. it's the client's responsibility to
|
||
- * define all the necessary states for the board being used.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_get_pinctrl(struct platform_device *pdev)
|
||
-{
|
||
- struct pinctrl *pinctrl;
|
||
-
|
||
- pinctrl = devm_pinctrl_get(&pdev->dev);
|
||
- if (IS_ERR(pinctrl)) {
|
||
- pr_err("%s: Unable to get pinctrl handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- tsc_device->pinctrl_info.pinctrl = pinctrl;
|
||
-
|
||
- /* get all the states handles */
|
||
- tsc_device->pinctrl_info.disable =
|
||
- pinctrl_lookup_state(pinctrl, "disable");
|
||
- tsc_device->pinctrl_info.ts0 =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-0");
|
||
- tsc_device->pinctrl_info.ts1 =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-1");
|
||
- tsc_device->pinctrl_info.dual_ts =
|
||
- pinctrl_lookup_state(pinctrl, "dual-ts");
|
||
- tsc_device->pinctrl_info.pc_card =
|
||
- pinctrl_lookup_state(pinctrl, "pc-card");
|
||
- tsc_device->pinctrl_info.ci_card =
|
||
- pinctrl_lookup_state(pinctrl, "ci-card");
|
||
- tsc_device->pinctrl_info.ci_plus =
|
||
- pinctrl_lookup_state(pinctrl, "ci-plus");
|
||
- tsc_device->pinctrl_info.ts0_pc_card =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-0-pc-card");
|
||
- tsc_device->pinctrl_info.ts0_ci_card =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-0-ci-card");
|
||
- tsc_device->pinctrl_info.ts0_ci_plus =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-0-ci-plus");
|
||
- tsc_device->pinctrl_info.ts1_pc_card =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-1-pc-card");
|
||
- tsc_device->pinctrl_info.ts1_ci_card =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-1-ci-card");
|
||
- tsc_device->pinctrl_info.ts1_ci_plus =
|
||
- pinctrl_lookup_state(pinctrl, "ts-in-1-ci-plus");
|
||
- tsc_device->pinctrl_info.dual_ts_pc_card =
|
||
- pinctrl_lookup_state(pinctrl, "dual-ts-pc-card");
|
||
- tsc_device->pinctrl_info.dual_ts_ci_card =
|
||
- pinctrl_lookup_state(pinctrl, "dual-ts-ci-card");
|
||
- tsc_device->pinctrl_info.dual_ts_ci_plus =
|
||
- pinctrl_lookup_state(pinctrl, "dual-ts-ci-plus");
|
||
-
|
||
- if (IS_ERR(tsc_device->pinctrl_info.disable)) {
|
||
- pr_err("%s: Unable to get pinctrl disable state handle\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Basic checks to inquire what pinctrl states are available */
|
||
- if (IS_ERR(tsc_device->pinctrl_info.ts0))
|
||
- tsc_device->pinctrl_info.is_ts0 = false;
|
||
- else
|
||
- tsc_device->pinctrl_info.is_ts0 = true;
|
||
-
|
||
- if (IS_ERR(tsc_device->pinctrl_info.ts1))
|
||
- tsc_device->pinctrl_info.is_ts1 = false;
|
||
- else
|
||
- tsc_device->pinctrl_info.is_ts1 = true;
|
||
-
|
||
- if (IS_ERR(tsc_device->pinctrl_info.pc_card) ||
|
||
- IS_ERR(tsc_device->pinctrl_info.ci_card) ||
|
||
- IS_ERR(tsc_device->pinctrl_info.ci_plus))
|
||
- tsc_device->pinctrl_info.is_pcmcia = false;
|
||
- else
|
||
- tsc_device->pinctrl_info.is_pcmcia = true;
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_get_regulator_bus() - Get the TSC regulator and register the bus client.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_get_regulator_bus(struct platform_device *pdev)
|
||
-{
|
||
- struct msm_bus_scale_pdata *tsc_bus_pdata = NULL;
|
||
-
|
||
- /* Reading the GDSC info */
|
||
- tsc_device->gdsc = devm_regulator_get(&pdev->dev, "vdd");
|
||
- if (IS_ERR(tsc_device->gdsc)) {
|
||
- dev_err(&pdev->dev, "%s: Failed to get vdd power regulator\n",
|
||
- __func__);
|
||
- return PTR_ERR(tsc_device->gdsc);
|
||
- }
|
||
-
|
||
- /* Reading the bus platform data */
|
||
- tsc_bus_pdata = msm_bus_cl_get_pdata(pdev);
|
||
- if (tsc_bus_pdata == NULL) {
|
||
- dev_err(&pdev->dev, "%s: Could not find the bus property. Continue anyway...\n",
|
||
- __func__);
|
||
- }
|
||
-
|
||
- /* Register the bus client */
|
||
- if (tsc_bus_pdata) {
|
||
- tsc_device->bus_client =
|
||
- msm_bus_scale_register_client(tsc_bus_pdata);
|
||
- if (!tsc_device->bus_client) {
|
||
- dev_err(&pdev->dev, "%s: Unable to register bus client\n",
|
||
- __func__);
|
||
- goto err;
|
||
- }
|
||
- }
|
||
-
|
||
- return 0;
|
||
-err:
|
||
- devm_regulator_put(tsc_device->gdsc);
|
||
- return -EINVAL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_get_irqs() - Get the TSC IRQ numbers and map the cam irq.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Read the irq numbers from the platform device information.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_get_irqs(struct platform_device *pdev)
|
||
-{
|
||
- int irq;
|
||
-
|
||
- irq = platform_get_irq_byname(pdev, "cam-cmd");
|
||
- if (irq > 0) {
|
||
- tsc_device->cam_cmd_irq = irq;
|
||
- } else {
|
||
- dev_err(&pdev->dev, "%s: Failed to get CAM_CMD IRQ = %d",
|
||
- __func__, irq);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- irq = platform_get_irq_byname(pdev, "card-detect");
|
||
- if (irq > 0) {
|
||
- tsc_device->card_detection_irq = irq;
|
||
- } else {
|
||
- dev_err(&pdev->dev, "%s: Failed to get CARD_DETECT IRQ = %d",
|
||
- __func__, irq);
|
||
- goto err;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-err:
|
||
- tsc_device->cam_cmd_irq = 0;
|
||
- tsc_device->card_detection_irq = 0;
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_map_io_memory() - Map memory resources to kernel space.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_map_io_memory(struct platform_device *pdev)
|
||
-{
|
||
- struct resource *registers_mem;
|
||
-
|
||
- /* Reading memory resources */
|
||
- registers_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||
- "tsc-base");
|
||
- if (!registers_mem) {
|
||
- dev_err(&pdev->dev, "%s: Missing tsc-base MEM resource",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- tsc_device->base = ioremap(registers_mem->start,
|
||
- resource_size(registers_mem));
|
||
- if (!tsc_device->base) {
|
||
- dev_err(&pdev->dev, "%s: ioremap failed", __func__);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_clocks_put() - Put the clocks
|
||
- */
|
||
-static void tsc_clocks_put(void)
|
||
-{
|
||
- if (tsc_device->ahb_clk)
|
||
- clk_put(tsc_device->ahb_clk);
|
||
- if (tsc_device->ci_clk)
|
||
- clk_put(tsc_device->ci_clk);
|
||
- if (tsc_device->ser_clk)
|
||
- clk_put(tsc_device->ser_clk);
|
||
- if (tsc_device->par_clk)
|
||
- clk_put(tsc_device->par_clk);
|
||
- if (tsc_device->cicam_ts_clk)
|
||
- clk_put(tsc_device->cicam_ts_clk);
|
||
- if (tsc_device->tspp2_core_clk)
|
||
- clk_put(tsc_device->tspp2_core_clk);
|
||
- if (tsc_device->vbif_tspp2_clk)
|
||
- clk_put(tsc_device->vbif_tspp2_clk);
|
||
- if (tsc_device->vbif_ahb_clk)
|
||
- clk_put(tsc_device->vbif_ahb_clk);
|
||
- if (tsc_device->vbif_axi_clk)
|
||
- clk_put(tsc_device->vbif_axi_clk);
|
||
-
|
||
- tsc_device->ahb_clk = NULL;
|
||
- tsc_device->ci_clk = NULL;
|
||
- tsc_device->ser_clk = NULL;
|
||
- tsc_device->par_clk = NULL;
|
||
- tsc_device->cicam_ts_clk = NULL;
|
||
- tsc_device->tspp2_core_clk = NULL;
|
||
- tsc_device->vbif_tspp2_clk = NULL;
|
||
- tsc_device->vbif_ahb_clk = NULL;
|
||
- tsc_device->vbif_axi_clk = NULL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_clocks_get() - Get the TSC clocks
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_clocks_get(struct platform_device *pdev)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- tsc_device->ahb_clk = clk_get(&pdev->dev, "bcc_tsc_ahb_clk");
|
||
- if (IS_ERR(tsc_device->ahb_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tsc_ahb_clk\n", __func__);
|
||
- ret = PTR_ERR(tsc_device->ahb_clk);
|
||
- goto ahb_err;
|
||
- }
|
||
-
|
||
- tsc_device->ci_clk = clk_get(&pdev->dev, "bcc_tsc_ci_clk");
|
||
- if (IS_ERR(tsc_device->ci_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tsc_ci_clk\n", __func__);
|
||
- ret = PTR_ERR(tsc_device->ci_clk);
|
||
- goto ci_err;
|
||
- }
|
||
-
|
||
- tsc_device->ser_clk = clk_get(&pdev->dev, "bcc_tsc_ser_clk");
|
||
- if (IS_ERR(tsc_device->ser_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tsc_ser_clk\n", __func__);
|
||
- ret = PTR_ERR(tsc_device->ser_clk);
|
||
- goto ser_err;
|
||
- }
|
||
-
|
||
- tsc_device->par_clk = clk_get(&pdev->dev, "bcc_tsc_par_clk");
|
||
- if (IS_ERR(tsc_device->par_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tsc_par_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->par_clk);
|
||
- goto par_err;
|
||
- }
|
||
-
|
||
- tsc_device->cicam_ts_clk = clk_get(&pdev->dev, "bcc_tsc_cicam_ts_clk");
|
||
- if (IS_ERR(tsc_device->cicam_ts_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tsc_cicam_ts_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->cicam_ts_clk);
|
||
- goto cicam_err;
|
||
- }
|
||
-
|
||
- tsc_device->tspp2_core_clk = clk_get(&pdev->dev, "bcc_tspp2_core_clk");
|
||
- if (IS_ERR(tsc_device->tspp2_core_clk)) {
|
||
- pr_err("%s: Failed to get bcc_tspp2_core_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->tspp2_core_clk);
|
||
- goto tspp2_err;
|
||
- }
|
||
-
|
||
- tsc_device->vbif_tspp2_clk = clk_get(&pdev->dev, "bcc_vbif_tspp2_clk");
|
||
- if (IS_ERR(tsc_device->vbif_tspp2_clk)) {
|
||
- pr_err("%s: Failed to get bcc_vbif_tspp2_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->vbif_tspp2_clk);
|
||
- goto vbif_tspp2_err;
|
||
- }
|
||
-
|
||
- tsc_device->vbif_ahb_clk = clk_get(&pdev->dev, "iface_vbif_clk");
|
||
- if (IS_ERR(tsc_device->vbif_ahb_clk)) {
|
||
- pr_err("%s: Failed to get bcc_vbif_ahb_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->vbif_ahb_clk);
|
||
- goto vbif_ahb_err;
|
||
- }
|
||
-
|
||
- tsc_device->vbif_axi_clk = clk_get(&pdev->dev, "vbif_core_clk");
|
||
- if (IS_ERR(tsc_device->vbif_axi_clk)) {
|
||
- pr_err("%s: Failed to get bcc_vbif_axi_clk", __func__);
|
||
- ret = PTR_ERR(tsc_device->vbif_axi_clk);
|
||
- goto vbif_axi_err;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-
|
||
-vbif_axi_err:
|
||
- tsc_device->vbif_axi_clk = NULL;
|
||
- clk_put(tsc_device->vbif_ahb_clk);
|
||
-vbif_ahb_err:
|
||
- tsc_device->vbif_ahb_clk = NULL;
|
||
- clk_put(tsc_device->vbif_tspp2_clk);
|
||
-vbif_tspp2_err:
|
||
- tsc_device->vbif_tspp2_clk = NULL;
|
||
- clk_put(tsc_device->tspp2_core_clk);
|
||
-tspp2_err:
|
||
- tsc_device->tspp2_core_clk = NULL;
|
||
- clk_put(tsc_device->cicam_ts_clk);
|
||
-cicam_err:
|
||
- tsc_device->cicam_ts_clk = NULL;
|
||
- clk_put(tsc_device->par_clk);
|
||
-par_err:
|
||
- tsc_device->par_clk = NULL;
|
||
- clk_put(tsc_device->ser_clk);
|
||
-ser_err:
|
||
- tsc_device->ser_clk = NULL;
|
||
- clk_put(tsc_device->ci_clk);
|
||
-ci_err:
|
||
- tsc_device->ci_clk = NULL;
|
||
- clk_put(tsc_device->ahb_clk);
|
||
-ahb_err:
|
||
- tsc_device->ahb_clk = NULL;
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_free_iommu_info() - Free IOMMU information.
|
||
- */
|
||
-static void tsc_free_iommu_info(void)
|
||
-{
|
||
- if (tsc_device->iommu_info.group) {
|
||
- iommu_group_put(tsc_device->iommu_info.group);
|
||
- tsc_device->iommu_info.group = NULL;
|
||
- }
|
||
-
|
||
- if (tsc_device->iommu_info.ion_client) {
|
||
- ion_client_destroy(tsc_device->iommu_info.ion_client);
|
||
- tsc_device->iommu_info.ion_client = NULL;
|
||
- }
|
||
-
|
||
- tsc_device->iommu_info.domain = NULL;
|
||
- tsc_device->iommu_info.domain_num = -1;
|
||
- tsc_device->iommu_info.partition_num = -1;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_get_iommu_info() - Get IOMMU information.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_get_iommu_info(struct platform_device *pdev)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- /* Create a new ION client used by tsc ci to allocate memory */
|
||
- tsc_device->iommu_info.ion_client = msm_ion_client_create("tsc_client");
|
||
- if (IS_ERR_OR_NULL(tsc_device->iommu_info.ion_client)) {
|
||
- pr_err("%s: error in ion_client_create", __func__);
|
||
- ret = PTR_ERR(tsc_device->iommu_info.ion_client);
|
||
- if (!ret)
|
||
- ret = -ENOMEM;
|
||
- tsc_device->iommu_info.ion_client = NULL;
|
||
- goto err_client;
|
||
- }
|
||
-
|
||
- /* Find the iommu group by the name obtained from the device tree */
|
||
- tsc_device->iommu_info.group =
|
||
- iommu_group_find(tsc_device->iommu_info.iommu_group_name);
|
||
- if (!tsc_device->iommu_info.group) {
|
||
- pr_err("%s: error in iommu_group_find", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_group;
|
||
- }
|
||
-
|
||
- /* Get the domain associated with the iommu group */
|
||
- tsc_device->iommu_info.domain =
|
||
- iommu_group_get_iommudata(tsc_device->iommu_info.group);
|
||
- if (IS_ERR_OR_NULL(tsc_device->iommu_info.domain)) {
|
||
- pr_err("%s: iommu_group_get_iommudata failed", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_domain;
|
||
- }
|
||
-
|
||
- /* Get the domain number */
|
||
- tsc_device->iommu_info.domain_num =
|
||
- msm_find_domain_no(tsc_device->iommu_info.domain);
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_domain:
|
||
- iommu_group_put(tsc_device->iommu_info.group);
|
||
- tsc_device->iommu_info.group = NULL;
|
||
-err_group:
|
||
- ion_client_destroy(tsc_device->iommu_info.ion_client);
|
||
- tsc_device->iommu_info.ion_client = NULL;
|
||
-err_client:
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_parse_dt() - Parse device-tree data and save it.
|
||
- *
|
||
- * @pdev: A pointer to the TSC platform device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tsc_parse_dt(struct platform_device *pdev)
|
||
-{
|
||
- struct device_node *node = pdev->dev.of_node;
|
||
- struct device_node *iommu_pnode;
|
||
- int ret;
|
||
-
|
||
- /* Check that power regulator property exist */
|
||
- if (!of_get_property(node, "vdd-supply", NULL)) {
|
||
- dev_err(&pdev->dev, "%s: Could not find vdd-supply property\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Reading IOMMU group label by obtaining the group's phandle */
|
||
- iommu_pnode = of_parse_phandle(node, "qcom,iommu-group", 0);
|
||
- if (!iommu_pnode) {
|
||
- dev_err(&pdev->dev, "%s: Couldn't find iommu-group property\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- ret = of_property_read_string(iommu_pnode, "label",
|
||
- &tsc_device->iommu_info.iommu_group_name);
|
||
- of_node_put(iommu_pnode);
|
||
- if (ret) {
|
||
- dev_err(&pdev->dev, "%s: Couldn't find label property of the IOMMU group, err=%d\n",
|
||
- __func__, ret);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Reading IOMMU partition */
|
||
- ret = of_property_read_u32(node, "qcom,iommu-partition",
|
||
- &tsc_device->iommu_info.partition_num);
|
||
- if (ret) {
|
||
- dev_err(&pdev->dev, "%s: Couldn't find iommu-partition property, err=%d\n",
|
||
- __func__, ret);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Reading reset cam gpio */
|
||
- tsc_device->reset_cam_gpio = of_get_named_gpio(node,
|
||
- "qcom,tsc-reset-cam-gpio", 0);
|
||
- if (tsc_device->reset_cam_gpio < 0) {
|
||
- dev_err(&pdev->dev, "%s: Couldn't find qcom,tsc-reset-cam-gpio property\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/* TSC Mux file operations */
|
||
-static const struct file_operations tsc_mux_fops = {
|
||
- .owner = THIS_MODULE,
|
||
- .open = tsc_mux_open,
|
||
- .poll = tsc_mux_poll,
|
||
- .release = tsc_mux_release,
|
||
- .unlocked_ioctl = tsc_mux_ioctl,
|
||
-};
|
||
-
|
||
-/* TSC CI file operations */
|
||
-static const struct file_operations tsc_ci_fops = {
|
||
- .owner = THIS_MODULE,
|
||
- .open = tsc_ci_open,
|
||
- .poll = tsc_ci_poll,
|
||
- .release = tsc_ci_release,
|
||
- .unlocked_ioctl = tsc_ci_ioctl,
|
||
-};
|
||
-
|
||
-
|
||
-/************************ Device driver probe function ************************/
|
||
-static int msm_tsc_probe(struct platform_device *pdev)
|
||
-{
|
||
- int ret;
|
||
-
|
||
- tsc_device = kzalloc(sizeof(struct tsc_device), GFP_KERNEL);
|
||
- if (!tsc_device) {
|
||
- pr_err("%s: Unable to allocate memory for struct\n", __func__);
|
||
- return -ENOMEM;
|
||
- }
|
||
-
|
||
- /* get information from device tree */
|
||
- if (pdev->dev.of_node) {
|
||
- ret = tsc_parse_dt(pdev);
|
||
- if (ret != 0) {
|
||
- pr_err("%s: devicetree data not available", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_dt;
|
||
- }
|
||
- } else { /* else - devicetree is not found */
|
||
- pr_err("%s: devicetree data is missing", __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_dt;
|
||
- }
|
||
-
|
||
- /* set up references */
|
||
- tsc_device->pdev = pdev;
|
||
- platform_set_drvdata(pdev, tsc_device);
|
||
-
|
||
- /* init iommu client, group and domain */
|
||
- if (!tsc_iommu_bypass) {
|
||
- ret = tsc_get_iommu_info(pdev);
|
||
- if (ret != 0)
|
||
- return ret;
|
||
- }
|
||
-
|
||
- /* Map clocks */
|
||
- ret = tsc_clocks_get(pdev);
|
||
- if (ret != 0)
|
||
- goto err_clocks_get;
|
||
-
|
||
- /* map registers memory */
|
||
- ret = tsc_map_io_memory(pdev);
|
||
- if (ret != 0)
|
||
- goto err_map_io;
|
||
-
|
||
- /* map irqs */
|
||
- ret = tsc_get_irqs(pdev);
|
||
- if (ret != 0)
|
||
- goto err_map_irqs;
|
||
-
|
||
- /* get regulators and bus */
|
||
- ret = tsc_get_regulator_bus(pdev);
|
||
- if (ret != 0)
|
||
- goto err_get_regulator_bus;
|
||
-
|
||
- /* get pinctrl */
|
||
- ret = tsc_get_pinctrl(pdev);
|
||
- if (ret != 0)
|
||
- goto err_pinctrl;
|
||
-
|
||
- /* creating the tsc device's class */
|
||
- tsc_class = class_create(THIS_MODULE, "tsc");
|
||
- if (IS_ERR(tsc_class)) {
|
||
- ret = PTR_ERR(tsc_class);
|
||
- pr_err("%s: Error creating class: %d\n", __func__, ret);
|
||
- goto err_class;
|
||
- }
|
||
-
|
||
- /* Initialize and register mux char device driver */
|
||
- ret = tsc_init_char_driver(&tsc_device->mux_chdev.cdev, &tsc_mux_fops,
|
||
- &tsc_device->mux_device_number, tsc_device->device_mux,
|
||
- "tsc_mux");
|
||
- if (ret != 0)
|
||
- goto err_chdev_mux;
|
||
-
|
||
- /* Initialize and register ci char device drivers */
|
||
- ret = tsc_init_char_driver(&tsc_device->ci_chdev.cdev, &tsc_ci_fops,
|
||
- &tsc_device->ci_device_number, tsc_device->device_ci,
|
||
- "tsc_ci");
|
||
- if (ret != 0)
|
||
- goto err_chdev_ci;
|
||
-
|
||
- /* Init char device counters */
|
||
- tsc_device->num_device_open = 0;
|
||
- tsc_device->num_mux_opened = 0;
|
||
- tsc_device->num_ci_opened = 0;
|
||
-
|
||
- /* Init char device mutexes and completion structs */
|
||
- mutex_init(&tsc_device->mux_chdev.mutex);
|
||
- mutex_init(&tsc_device->ci_chdev.mutex);
|
||
- mutex_init(&tsc_device->mutex);
|
||
- init_completion(&tsc_device->ci_chdev.transaction_complete);
|
||
- init_completion(&tsc_device->ci_chdev.transaction_finish);
|
||
-
|
||
- /* Init debugfs support */
|
||
- tsc_debugfs_init();
|
||
-
|
||
- return ret;
|
||
-
|
||
-err_chdev_ci:
|
||
- device_destroy(tsc_class, tsc_device->mux_chdev.cdev.dev);
|
||
- cdev_del(&tsc_device->mux_chdev.cdev);
|
||
-err_chdev_mux:
|
||
- class_destroy(tsc_class);
|
||
-err_class:
|
||
-err_pinctrl:
|
||
- if (tsc_device->bus_client)
|
||
- msm_bus_scale_unregister_client(tsc_device->bus_client);
|
||
-
|
||
- devm_regulator_put(tsc_device->gdsc);
|
||
-err_get_regulator_bus:
|
||
-err_map_irqs:
|
||
- iounmap(tsc_device->base);
|
||
-err_map_io:
|
||
- tsc_clocks_put();
|
||
-err_clocks_get:
|
||
- tsc_free_iommu_info();
|
||
-err_dt:
|
||
- kfree(tsc_device);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/*********************** Device driver remove function ***********************/
|
||
-static int msm_tsc_remove(struct platform_device *pdev)
|
||
-{
|
||
- /* Removing debugfs support */
|
||
- tsc_debugfs_exit();
|
||
-
|
||
- /* Destroying the char device mutexes */
|
||
- mutex_destroy(&tsc_device->mux_chdev.mutex);
|
||
- mutex_destroy(&tsc_device->ci_chdev.mutex);
|
||
-
|
||
- /* unregistering and deleting the tsc-ci char device driver*/
|
||
- device_destroy(tsc_class, tsc_device->ci_chdev.cdev.dev);
|
||
- cdev_del(&tsc_device->ci_chdev.cdev);
|
||
-
|
||
- /* unregistering and deleting the tsc-mux char device driver*/
|
||
- device_destroy(tsc_class, tsc_device->mux_chdev.cdev.dev);
|
||
- cdev_del(&tsc_device->mux_chdev.cdev);
|
||
-
|
||
- /* Unregistering the char devices */
|
||
- unregister_chrdev_region(tsc_device->ci_device_number, 1);
|
||
- unregister_chrdev_region(tsc_device->mux_device_number, 1);
|
||
-
|
||
- /* Removing the tsc class*/
|
||
- class_destroy(tsc_class);
|
||
-
|
||
- /* Unregister the bus client and the regulator */
|
||
- if (tsc_device->bus_client)
|
||
- msm_bus_scale_unregister_client(tsc_device->bus_client);
|
||
-
|
||
- devm_regulator_put(tsc_device->gdsc);
|
||
-
|
||
- /* Unmapping the io memory */
|
||
- iounmap(tsc_device->base);
|
||
-
|
||
- /* Releasing the clocks */
|
||
- tsc_clocks_put();
|
||
-
|
||
- /* Releasing the iommu info */
|
||
- if (!tsc_iommu_bypass)
|
||
- tsc_free_iommu_info();
|
||
-
|
||
- /* Releasing the memory allocated for the TSC device struct */
|
||
- kfree(tsc_device);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/*********************** Platform driver information ***********************/
|
||
-static struct of_device_id msm_match_table[] = {
|
||
- {.compatible = "qcom,msm-tsc"},
|
||
- {}
|
||
-};
|
||
-
|
||
-static struct platform_driver msm_tsc_driver = {
|
||
- .probe = msm_tsc_probe,
|
||
- .remove = msm_tsc_remove,
|
||
- .driver = {
|
||
- .name = "msm_tsc",
|
||
- .of_match_table = msm_match_table,
|
||
- },
|
||
-};
|
||
-
|
||
-/**
|
||
- * tsc_init() - TSC driver module init function.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int __init tsc_init(void)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- /* register the driver, and check hardware */
|
||
- ret = platform_driver_register(&msm_tsc_driver);
|
||
- if (ret) {
|
||
- pr_err("%s: platform_driver_register failed: %d\n", __func__,
|
||
- ret);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsc_exit() - TSC driver module exit function.
|
||
- */
|
||
-static void __exit tsc_exit(void)
|
||
-{
|
||
- platform_driver_unregister(&msm_tsc_driver);
|
||
-}
|
||
-
|
||
-module_init(tsc_init);
|
||
-module_exit(tsc_exit);
|
||
-
|
||
-MODULE_DESCRIPTION("TSC platform device and two char devs: mux and ci");
|
||
-MODULE_LICENSE("GPL v2");
|
||
diff --git a/drivers/media/platform/msm/broadcast/tspp2.c b/drivers/media/platform/msm/broadcast/tspp2.c
|
||
deleted file mode 100644
|
||
index 1f51dca..0000000
|
||
--- a/drivers/media/platform/msm/broadcast/tspp2.c
|
||
+++ /dev/null
|
||
@@ -1,8578 +0,0 @@
|
||
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||
- *
|
||
- * This program is free software; you can redistribute it and/or modify
|
||
- * it under the terms of the GNU General Public License version 2 and
|
||
- * only version 2 as published by the Free Software Foundation.
|
||
- *
|
||
- * This program 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.
|
||
- */
|
||
-
|
||
-#include <linux/module.h>
|
||
-#include <linux/clk.h>
|
||
-#include <linux/io.h>
|
||
-#include <linux/dma-mapping.h>
|
||
-#include <linux/debugfs.h>
|
||
-#include <linux/device.h>
|
||
-#include <linux/pm_runtime.h>
|
||
-#include <linux/pm_wakeup.h>
|
||
-#include <linux/platform_device.h>
|
||
-#include <linux/msm_ion.h>
|
||
-#include <linux/slab.h>
|
||
-#include <linux/delay.h>
|
||
-#include <linux/of.h>
|
||
-#include <linux/list.h>
|
||
-#include <linux/mutex.h>
|
||
-#include <linux/workqueue.h>
|
||
-#include <linux/iommu.h>
|
||
-#include <linux/qcom_iommu.h>
|
||
-#include <linux/msm_iommu_domains.h>
|
||
-#include <linux/msm-bus.h>
|
||
-#include <mach/msm_tspp2.h>
|
||
-#include <linux/clk/msm-clk.h>
|
||
-
|
||
-#define TSPP2_MODULUS_OP(val, mod) ((val) & ((mod) - 1))
|
||
-
|
||
-/* General definitions. Note we're reserving one batch. */
|
||
-#define TSPP2_NUM_ALL_INPUTS (TSPP2_NUM_TSIF_INPUTS + TSPP2_NUM_MEM_INPUTS)
|
||
-#define TSPP2_NUM_CONTEXTS 128
|
||
-#define TSPP2_NUM_AVAIL_CONTEXTS 127
|
||
-#define TSPP2_NUM_HW_FILTERS 128
|
||
-#define TSPP2_NUM_BATCHES 15
|
||
-#define TSPP2_FILTERS_PER_BATCH 8
|
||
-#define TSPP2_NUM_AVAIL_FILTERS (TSPP2_NUM_HW_FILTERS - TSPP2_FILTERS_PER_BATCH)
|
||
-#define TSPP2_NUM_KEYTABLES 32
|
||
-#define TSPP2_TSIF_DEF_TIME_LIMIT 15000 /* Number of tsif-ref-clock ticks */
|
||
-
|
||
-#define TSPP2_NUM_EVENT_WORK_ELEMENTS 256
|
||
-
|
||
-/*
|
||
- * Based on the hardware programming guide, HW requires we wait for up to 2ms
|
||
- * before closing the pipes used by the filter.
|
||
- * This is required to avoid unexpected pipe reset interrupts.
|
||
- */
|
||
-#define TSPP2_HW_DELAY_USEC 2000
|
||
-
|
||
-/*
|
||
- * Default source configuration:
|
||
- * Sync byte 0x47, check sync byte,
|
||
- * Do not monitor scrambling bits,
|
||
- * Discard packets with invalid AF,
|
||
- * Do not assume duplicates,
|
||
- * Do not ignore discontinuity indicator,
|
||
- * Check continuity of TS packets.
|
||
- */
|
||
-#define TSPP2_DEFAULT_SRC_CONFIG 0x47801E49
|
||
-
|
||
-/*
|
||
- * Default memory source configuration:
|
||
- * Use 16 batches,
|
||
- * Attach last batch to each memory source.
|
||
- */
|
||
-#define TSPP2_DEFAULT_MEM_SRC_CONFIG 0x80000010
|
||
-
|
||
-/* Bypass VBIF/IOMMU for debug and bring-up purposes */
|
||
-static int tspp2_iommu_bypass;
|
||
-module_param(tspp2_iommu_bypass, int, S_IRUGO);
|
||
-
|
||
-/* Enable Invalid Adaptation Field control bits event */
|
||
-static int tspp2_en_invalid_af_ctrl;
|
||
-module_param(tspp2_en_invalid_af_ctrl, int, S_IRUGO | S_IWUSR);
|
||
-
|
||
-/* Enable Invalid Adaptation Field length event */
|
||
-static int tspp2_en_invalid_af_length;
|
||
-module_param(tspp2_en_invalid_af_length, int, S_IRUGO | S_IWUSR);
|
||
-
|
||
-/* Enable PES No Sync event */
|
||
-static int tspp2_en_pes_no_sync;
|
||
-module_param(tspp2_en_pes_no_sync, int, S_IRUGO | S_IWUSR);
|
||
-
|
||
-/**
|
||
- * enum tspp2_operation_opcode - TSPP2 Operation opcode for TSPP2_OPCODE
|
||
- */
|
||
-enum tspp2_operation_opcode {
|
||
- TSPP2_OPCODE_PES_ANALYSIS = 0x03,
|
||
- TSPP2_OPCODE_RAW_TRANSMIT = 0x07,
|
||
- TSPP2_OPCODE_PES_TRANSMIT = 0x00,
|
||
- TSPP2_OPCODE_PCR_EXTRACTION = 0x05,
|
||
- TSPP2_OPCODE_CIPHER = 0x01,
|
||
- TSPP2_OPCODE_INDEXING = 0x09,
|
||
- TSPP2_OPCODE_COPY_PACKET = 0x0B,
|
||
- TSPP2_OPCODE_EXIT = 0x0F
|
||
-};
|
||
-
|
||
-/* TSIF Register definitions: */
|
||
-#define TSPP2_TSIF_STS_CTL (0x0000)
|
||
-#define TSPP2_TSIF_TIME_LIMIT (0x0004)
|
||
-#define TSPP2_TSIF_CLK_REF (0x0008)
|
||
-#define TSPP2_TSIF_LPBK_FLAGS (0x000C)
|
||
-#define TSPP2_TSIF_LPBK_DATA (0x0010)
|
||
-#define TSPP2_TSIF_DATA_PORT (0x0100)
|
||
-
|
||
-/* Bits for TSPP2_TSIF_STS_CTL register */
|
||
-#define TSIF_STS_CTL_PKT_WRITE_ERR BIT(30)
|
||
-#define TSIF_STS_CTL_PKT_READ_ERR BIT(29)
|
||
-#define TSIF_STS_CTL_EN_IRQ BIT(28)
|
||
-#define TSIF_STS_CTL_PACK_AVAIL BIT(27)
|
||
-#define TSIF_STS_CTL_1ST_PACKET BIT(26)
|
||
-#define TSIF_STS_CTL_OVERFLOW BIT(25)
|
||
-#define TSIF_STS_CTL_LOST_SYNC BIT(24)
|
||
-#define TSIF_STS_CTL_TIMEOUT BIT(23)
|
||
-#define TSIF_STS_CTL_INV_SYNC BIT(21)
|
||
-#define TSIF_STS_CTL_INV_NULL BIT(20)
|
||
-#define TSIF_STS_CTL_INV_ERROR BIT(19)
|
||
-#define TSIF_STS_CTL_INV_ENABLE BIT(18)
|
||
-#define TSIF_STS_CTL_INV_DATA BIT(17)
|
||
-#define TSIF_STS_CTL_INV_CLOCK BIT(16)
|
||
-#define TSIF_STS_CTL_PARALLEL BIT(14)
|
||
-#define TSIF_STS_CTL_EN_NULL BIT(11)
|
||
-#define TSIF_STS_CTL_EN_ERROR BIT(10)
|
||
-#define TSIF_STS_CTL_LAST_BIT BIT(9)
|
||
-#define TSIF_STS_CTL_EN_TIME_LIM BIT(8)
|
||
-#define TSIF_STS_CTL_EN_TCR BIT(7)
|
||
-#define TSIF_STS_CTL_TEST_MODE BIT(6)
|
||
-#define TSIF_STS_CTL_MODE_2 BIT(5)
|
||
-#define TSIF_STS_CTL_EN_DM BIT(4)
|
||
-#define TSIF_STS_CTL_STOP BIT(3)
|
||
-#define TSIF_STS_CTL_START BIT(0)
|
||
-
|
||
-/* Indexing Table Register definitions: id = 0..3, n = 0..25 */
|
||
-#define TSPP2_INDEX_TABLE_PREFIX(id) (0x6000 + ((id) << 2))
|
||
-#define TSPP2_INDEX_TABLE_PREFIX_MASK(id) (0x6010 + ((id) << 2))
|
||
-#define TSPP2_INDEX_TABLE_PATTEREN(id, n) (0x3C00 + ((id) << 8) + \
|
||
- ((n) << 3))
|
||
-#define TSPP2_INDEX_TABLE_MASK(id, n) (0x3C04 + ((id) << 8) + \
|
||
- ((n) << 3))
|
||
-#define TSPP2_INDEX_TABLE_PARAMS(id) (0x6020 + ((id) << 2))
|
||
-
|
||
-/* Bits for TSPP2_INDEX_TABLE_PARAMS register */
|
||
-#define INDEX_TABLE_PARAMS_PREFIX_SIZE_OFFS 8
|
||
-#define INDEX_TABLE_PARAMS_NUM_PATTERNS_OFFS 0
|
||
-
|
||
-/* Source with memory input register definitions: n = 0..7 */
|
||
-#define TSPP2_MEM_INPUT_SRC_CONFIG(n) (0x6040 + ((n) << 2))
|
||
-
|
||
-/* Bits for TSPP2_MEM_INPUT_SRC_CONFIG register */
|
||
-#define MEM_INPUT_SRC_CONFIG_BATCHES_OFFS 16
|
||
-#define MEM_INPUT_SRC_CONFIG_INPUT_PIPE_OFFS 8
|
||
-#define MEM_INPUT_SRC_CONFIG_16_BATCHES_OFFS 4
|
||
-#define MEM_INPUT_SRC_CONFIG_STAMP_SUFFIX_OFFS 2
|
||
-#define MEM_INPUT_SRC_CONFIG_STAMP_EN_OFFS 1
|
||
-#define MEM_INPUT_SRC_CONFIG_INPUT_EN_OFFS 0
|
||
-
|
||
-/* Source with TSIF input register definitions: n = 0..1 */
|
||
-#define TSPP2_TSIF_INPUT_SRC_CONFIG(n) (0x6060 + ((n) << 2))
|
||
-#define TSIF_INPUT_SRC_CONFIG_16_BATCHES_OFFS 4
|
||
-
|
||
-/* Bits for TSPP2_TSIF_INPUT_SRC_CONFIG register */
|
||
-#define TSIF_INPUT_SRC_CONFIG_BATCHES_OFFS 16
|
||
-#define TSIF_INPUT_SRC_CONFIG_INPUT_EN_OFFS 0
|
||
-
|
||
-/* Source with any input register definitions: n = 0..9 */
|
||
-#define TSPP2_SRC_DEST_PIPES(n) (0x6070 + ((n) << 2))
|
||
-#define TSPP2_SRC_CONFIG(n) (0x6120 + ((n) << 2))
|
||
-#define TSPP2_SRC_TOTAL_TSP(n) (0x6600 + ((n) << 2))
|
||
-#define TSPP2_SRC_FILTERED_OUT_TSP(n) (0x6630 + ((n) << 2))
|
||
-
|
||
-/* Bits for TSPP2_SRC_CONFIG register */
|
||
-#define SRC_CONFIG_SYNC_BYTE_OFFS 24
|
||
-#define SRC_CONFIG_CHECK_SYNC_OFFS 23
|
||
-#define SRC_CONFIG_SCRAMBLING_MONITOR_OFFS 13
|
||
-#define SRC_CONFIG_VERIFY_PES_START_OFFS 12
|
||
-#define SRC_CONFIG_SCRAMBLING3_OFFS 10
|
||
-#define SRC_CONFIG_SCRAMBLING2_OFFS 8
|
||
-#define SRC_CONFIG_SCRAMBLING1_OFFS 6
|
||
-#define SRC_CONFIG_SCRAMBLING0_OFFS 4
|
||
-#define SRC_CONFIG_DISCARD_INVALID_AF_OFFS 3
|
||
-#define SRC_CONFIG_ASSUME_DUPLICATES_OFFS 2
|
||
-#define SRC_CONFIG_IGNORE_DISCONT_OFFS 1
|
||
-#define SRC_CONFIG_CHECK_CONT_OFFS 0
|
||
-
|
||
-/* Context register definitions: n = 0..127 */
|
||
-#define TSPP2_PES_CONTEXT0(n) (0x0000 + ((n) << 4))
|
||
-#define TSPP2_PES_CONTEXT1(n) (0x0004 + ((n) << 4))
|
||
-#define TSPP2_PES_CONTEXT2(n) (0x0008 + ((n) << 4))
|
||
-#define TSPP2_PES_CONTEXT3(n) (0x000C + ((n) << 4))
|
||
-#define TSPP2_INDEXING_CONTEXT0(n) (0x0800 + ((n) << 3))
|
||
-#define TSPP2_INDEXING_CONTEXT1(n) (0x0804 + ((n) << 3))
|
||
-#define TSPP2_TSP_CONTEXT(n) (0x5600 + ((n) << 2))
|
||
-
|
||
-/* Bits for TSPP2_TSP_CONTEXT register */
|
||
-#define TSP_CONTEXT_TS_HEADER_SC_OFFS 6
|
||
-#define TSP_CONTEXT_PES_HEADER_SC_OFFS 8
|
||
-
|
||
-/* Operations register definitions: f_idx = 0..127, n = 0..15 */
|
||
-#define TSPP2_OPCODE(f_idx, n) (0x1000 + \
|
||
- ((f_idx) * (TSPP2_MAX_OPS_PER_FILTER << 2)) + \
|
||
- ((n) << 2))
|
||
-
|
||
-/* Filter register definitions: n = 0..127 */
|
||
-#define TSPP2_FILTER_ENTRY0(n) (0x5800 + ((n) << 3))
|
||
-#define TSPP2_FILTER_ENTRY1(n) (0x5804 + ((n) << 3))
|
||
-
|
||
-/* Bits for TSPP2_FILTER_ENTRY0 register */
|
||
-#define FILTER_ENTRY0_PID_OFFS 0
|
||
-#define FILTER_ENTRY0_MASK_OFFS 13
|
||
-#define FILTER_ENTRY0_EN_OFFS 26
|
||
-#define FILTER_ENTRY0_CODEC_OFFS 27
|
||
-
|
||
-/* Bits for TSPP2_FILTER_ENTRY1 register */
|
||
-#define FILTER_ENTRY1_CONTEXT_OFFS 0
|
||
-
|
||
-/* Filter context-based counter register definitions: n = 0..127 */
|
||
-#define TSPP2_FILTER_TSP_SYNC_ERROR(n) (0x4000 + ((n) << 2))
|
||
-#define TSPP2_FILTER_ERRED_TSP(n) (0x4200 + ((n) << 2))
|
||
-#define TSPP2_FILTER_DISCONTINUITIES(n) (0x4400 + ((n) << 2))
|
||
-#define TSPP2_FILTER_SCRAMBLING_BITS_DISCARD(n) (0x4600 + ((n) << 2))
|
||
-#define TSPP2_FILTER_TSP_TOTAL_NUM(n) (0x4800 + ((n) << 2))
|
||
-#define TSPP2_FILTER_DISCONT_INDICATOR(n) (0x4A00 + ((n) << 2))
|
||
-#define TSPP2_FILTER_TSP_NO_PAYLOAD(n) (0x4C00 + ((n) << 2))
|
||
-#define TSPP2_FILTER_TSP_DUPLICATE(n) (0x4E00 + ((n) << 2))
|
||
-#define TSPP2_FILTER_KEY_FETCH_FAILURE(n) (0x5000 + ((n) << 2))
|
||
-#define TSPP2_FILTER_DROPPED_PCR(n) (0x5200 + ((n) << 2))
|
||
-#define TSPP2_FILTER_PES_ERRORS(n) (0x5400 + ((n) << 2))
|
||
-
|
||
-/* Pipe register definitions: n = 0..30 */
|
||
-#define TSPP2_PIPE_THRESH_CONFIG(n) (0x60A0 + ((n) << 2))
|
||
-#define TSPP2_PIPE_LAST_ADDRESS(n) (0x6190 + ((n) << 2))
|
||
-#define TSPP2_PIPE_SECURITY 0x6150
|
||
-#define TSPP2_DATA_NOT_SENT_ON_PIPE(n) (0x6660 + ((n) << 2))
|
||
-
|
||
-/* Global register definitions: */
|
||
-#define TSPP2_PCR_GLOBAL_CONFIG 0x6160
|
||
-#define TSPP2_CLK_TO_PCR_TIME_UNIT 0x6170
|
||
-#define TSPP2_DESC_WAIT_TIMEOUT 0x6180
|
||
-#define TSPP2_GLOBAL_IRQ_STATUS 0x6300
|
||
-#define TSPP2_GLOBAL_IRQ_CLEAR 0x6304
|
||
-#define TSPP2_GLOBAL_IRQ_ENABLE 0x6308
|
||
-#define TSPP2_KEY_NOT_READY_IRQ_STATUS 0x6310
|
||
-#define TSPP2_KEY_NOT_READY_IRQ_CLEAR 0x6314
|
||
-#define TSPP2_KEY_NOT_READY_IRQ_ENABLE 0x6318
|
||
-#define TSPP2_UNEXPECTED_RST_IRQ_STATUS 0x6320
|
||
-#define TSPP2_UNEXPECTED_RST_IRQ_CLEAR 0x6324
|
||
-#define TSPP2_UNEXPECTED_RST_IRQ_ENABLE 0x6328
|
||
-#define TSPP2_WRONG_PIPE_DIR_IRQ_STATUS 0x6330
|
||
-#define TSPP2_WRONG_PIPE_DIR_IRQ_CLEAR 0x6334
|
||
-#define TSPP2_WRONG_PIPE_DIR_IRQ_ENABLE 0x6338
|
||
-#define TSPP2_QSB_RESPONSE_ERROR_IRQ_STATUS 0x6340
|
||
-#define TSPP2_QSB_RESPONSE_ERROR_IRQ_CLEAR 0x6344
|
||
-#define TSPP2_QSB_RESPONSE_ERROR_IRQ_ENABLE 0x6348
|
||
-#define TSPP2_SRC_TOTAL_TSP_RESET 0x6710
|
||
-#define TSPP2_SRC_FILTERED_OUT_TSP_RESET 0x6714
|
||
-#define TSPP2_DATA_NOT_SENT_ON_PIPE_RESET 0x6718
|
||
-#define TSPP2_VERSION 0x6FFC
|
||
-
|
||
-/* Bits for TSPP2_GLOBAL_IRQ_CLEAR register */
|
||
-#define GLOBAL_IRQ_CLEAR_RESERVED_OFFS 4
|
||
-
|
||
-/* Bits for TSPP2_VERSION register */
|
||
-#define VERSION_MAJOR_OFFS 28
|
||
-#define VERSION_MINOR_OFFS 16
|
||
-#define VERSION_STEP_OFFS 0
|
||
-
|
||
-/* Bits for TSPP2_GLOBAL_IRQ_XXX registers */
|
||
-#define GLOBAL_IRQ_TSP_INVALID_AF_OFFS 0
|
||
-#define GLOBAL_IRQ_TSP_INVALID_LEN_OFFS 1
|
||
-#define GLOBAL_IRQ_PES_NO_SYNC_OFFS 2
|
||
-#define GLOBAL_IRQ_ENCRYPT_LEVEL_ERR_OFFS 3
|
||
-#define GLOBAL_IRQ_KEY_NOT_READY_OFFS 4
|
||
-#define GLOBAL_IRQ_UNEXPECTED_RESET_OFFS 5
|
||
-#define GLOBAL_IRQ_QSB_RESP_ERR_OFFS 6
|
||
-#define GLOBAL_IRQ_WRONG_PIPE_DIR_OFFS 7
|
||
-#define GLOBAL_IRQ_SC_GO_HIGH_OFFS 8
|
||
-#define GLOBAL_IRQ_SC_GO_LOW_OFFS 9
|
||
-#define GLOBAL_IRQ_READ_FAIL_OFFS 16
|
||
-#define GLOBAL_IRQ_FC_STALL_OFFS 24
|
||
-
|
||
-/* Bits for TSPP2_PCR_GLOBAL_CONFIG register */
|
||
-#define PCR_GLOBAL_CONFIG_PCR_ON_DISCONT_OFFS 10
|
||
-#define PCR_GLOBAL_CONFIG_STC_OFFSET_OFFS 8
|
||
-#define PCR_GLOBAL_CONFIG_PCR_INTERVAL_OFFS 0
|
||
-#define PCR_GLOBAL_CONFIG_PCR_ON_DISCONT BIT(10)
|
||
-#define PCR_GLOBAL_CONFIG_STC_OFFSET (BIT(8)|BIT(9))
|
||
-#define PCR_GLOBAL_CONFIG_PCR_INTERVAL 0xFF
|
||
-
|
||
-/* n = 0..3, each register handles 32 filters */
|
||
-#define TSPP2_SC_GO_HIGH_STATUS(n) (0x6350 + ((n) << 2))
|
||
-#define TSPP2_SC_GO_HIGH_CLEAR(n) (0x6360 + ((n) << 2))
|
||
-#define TSPP2_SC_GO_HIGH_ENABLE(n) (0x6370 + ((n) << 2))
|
||
-#define TSPP2_SC_GO_LOW_STATUS(n) (0x6390 + ((n) << 2))
|
||
-#define TSPP2_SC_GO_LOW_CLEAR(n) (0x63A0 + ((n) << 2))
|
||
-#define TSPP2_SC_GO_LOW_ENABLE(n) (0x63B0 + ((n) << 2))
|
||
-
|
||
-/* n = 0..3, each register handles 32 contexts */
|
||
-#define TSPP2_TSP_CONTEXT_RESET(n) (0x6500 + ((n) << 2))
|
||
-#define TSPP2_PES_CONTEXT_RESET(n) (0x6510 + ((n) << 2))
|
||
-#define TSPP2_INDEXING_CONTEXT_RESET(n) (0x6520 + ((n) << 2))
|
||
-
|
||
-/* debugfs entries */
|
||
-
|
||
-#define TSPP2_S_RW (S_IRUGO | S_IWUSR)
|
||
-
|
||
-struct debugfs_entry {
|
||
- const char *name;
|
||
- mode_t mode;
|
||
- int offset;
|
||
-};
|
||
-
|
||
-static const struct debugfs_entry tsif_regs[] = {
|
||
- {"sts_ctl", TSPP2_S_RW, TSPP2_TSIF_STS_CTL},
|
||
- {"time_limit", TSPP2_S_RW, TSPP2_TSIF_TIME_LIMIT},
|
||
- {"clk_ref", TSPP2_S_RW, TSPP2_TSIF_CLK_REF},
|
||
- {"lpbk_flags", TSPP2_S_RW, TSPP2_TSIF_LPBK_FLAGS},
|
||
- {"lpbk_data", TSPP2_S_RW, TSPP2_TSIF_LPBK_DATA},
|
||
- {"data_port", S_IRUGO, TSPP2_TSIF_DATA_PORT},
|
||
-};
|
||
-
|
||
-static const struct debugfs_entry tspp2_regs[] = {
|
||
- /* Memory input source configuration registers */
|
||
- {"mem_input_src_config_0", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(0)},
|
||
- {"mem_input_src_config_1", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(1)},
|
||
- {"mem_input_src_config_2", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(2)},
|
||
- {"mem_input_src_config_3", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(3)},
|
||
- {"mem_input_src_config_4", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(4)},
|
||
- {"mem_input_src_config_5", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(5)},
|
||
- {"mem_input_src_config_6", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(6)},
|
||
- {"mem_input_src_config_7", TSPP2_S_RW, TSPP2_MEM_INPUT_SRC_CONFIG(7)},
|
||
- /* TSIF input source configuration registers */
|
||
- {"tsif_input_src_config_0", TSPP2_S_RW, TSPP2_TSIF_INPUT_SRC_CONFIG(0)},
|
||
- {"tsif_input_src_config_1", TSPP2_S_RW, TSPP2_TSIF_INPUT_SRC_CONFIG(1)},
|
||
- /* Source destination pipes association registers */
|
||
- {"src_dest_pipes_0", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(0)},
|
||
- {"src_dest_pipes_1", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(1)},
|
||
- {"src_dest_pipes_2", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(2)},
|
||
- {"src_dest_pipes_3", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(3)},
|
||
- {"src_dest_pipes_4", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(4)},
|
||
- {"src_dest_pipes_5", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(5)},
|
||
- {"src_dest_pipes_6", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(6)},
|
||
- {"src_dest_pipes_7", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(7)},
|
||
- {"src_dest_pipes_8", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(8)},
|
||
- {"src_dest_pipes_9", TSPP2_S_RW, TSPP2_SRC_DEST_PIPES(9)},
|
||
- /* Source configuration registers */
|
||
- {"src_config_0", TSPP2_S_RW, TSPP2_SRC_CONFIG(0)},
|
||
- {"src_config_1", TSPP2_S_RW, TSPP2_SRC_CONFIG(1)},
|
||
- {"src_config_2", TSPP2_S_RW, TSPP2_SRC_CONFIG(2)},
|
||
- {"src_config_3", TSPP2_S_RW, TSPP2_SRC_CONFIG(3)},
|
||
- {"src_config_4", TSPP2_S_RW, TSPP2_SRC_CONFIG(4)},
|
||
- {"src_config_5", TSPP2_S_RW, TSPP2_SRC_CONFIG(5)},
|
||
- {"src_config_6", TSPP2_S_RW, TSPP2_SRC_CONFIG(6)},
|
||
- {"src_config_7", TSPP2_S_RW, TSPP2_SRC_CONFIG(7)},
|
||
- {"src_config_8", TSPP2_S_RW, TSPP2_SRC_CONFIG(8)},
|
||
- {"src_config_9", TSPP2_S_RW, TSPP2_SRC_CONFIG(9)},
|
||
- /* Source total TS packets counter registers */
|
||
- {"src_total_tsp_0", S_IRUGO, TSPP2_SRC_TOTAL_TSP(0)},
|
||
- {"src_total_tsp_1", S_IRUGO, TSPP2_SRC_TOTAL_TSP(1)},
|
||
- {"src_total_tsp_2", S_IRUGO, TSPP2_SRC_TOTAL_TSP(2)},
|
||
- {"src_total_tsp_3", S_IRUGO, TSPP2_SRC_TOTAL_TSP(3)},
|
||
- {"src_total_tsp_4", S_IRUGO, TSPP2_SRC_TOTAL_TSP(4)},
|
||
- {"src_total_tsp_5", S_IRUGO, TSPP2_SRC_TOTAL_TSP(5)},
|
||
- {"src_total_tsp_6", S_IRUGO, TSPP2_SRC_TOTAL_TSP(6)},
|
||
- {"src_total_tsp_7", S_IRUGO, TSPP2_SRC_TOTAL_TSP(7)},
|
||
- {"src_total_tsp_8", S_IRUGO, TSPP2_SRC_TOTAL_TSP(8)},
|
||
- {"src_total_tsp_9", S_IRUGO, TSPP2_SRC_TOTAL_TSP(9)},
|
||
- /* Source total filtered out TS packets counter registers */
|
||
- {"src_filtered_out_tsp_0", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(0)},
|
||
- {"src_filtered_out_tsp_1", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(1)},
|
||
- {"src_filtered_out_tsp_2", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(2)},
|
||
- {"src_filtered_out_tsp_3", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(3)},
|
||
- {"src_filtered_out_tsp_4", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(4)},
|
||
- {"src_filtered_out_tsp_5", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(5)},
|
||
- {"src_filtered_out_tsp_6", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(6)},
|
||
- {"src_filtered_out_tsp_7", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(7)},
|
||
- {"src_filtered_out_tsp_8", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(8)},
|
||
- {"src_filtered_out_tsp_9", S_IRUGO, TSPP2_SRC_FILTERED_OUT_TSP(9)},
|
||
- /* Global registers */
|
||
- {"pipe_security", TSPP2_S_RW, TSPP2_PIPE_SECURITY},
|
||
- {"pcr_global_config", TSPP2_S_RW, TSPP2_PCR_GLOBAL_CONFIG},
|
||
- {"clk_to_pcr_time_unit", TSPP2_S_RW, TSPP2_CLK_TO_PCR_TIME_UNIT},
|
||
- {"desc_wait_timeout", TSPP2_S_RW, TSPP2_DESC_WAIT_TIMEOUT},
|
||
- {"global_irq_status", S_IRUGO, TSPP2_GLOBAL_IRQ_STATUS},
|
||
- {"global_irq_clear", S_IWUSR, TSPP2_GLOBAL_IRQ_CLEAR},
|
||
- {"global_irq_en", TSPP2_S_RW, TSPP2_GLOBAL_IRQ_ENABLE},
|
||
- {"key_not_ready_irq_status", S_IRUGO, TSPP2_KEY_NOT_READY_IRQ_STATUS},
|
||
- {"key_not_ready_irq_clear", S_IWUSR, TSPP2_KEY_NOT_READY_IRQ_CLEAR},
|
||
- {"key_not_ready_irq_en", TSPP2_S_RW, TSPP2_KEY_NOT_READY_IRQ_ENABLE},
|
||
- {"unexpected_rst_irq_status", S_IRUGO, TSPP2_UNEXPECTED_RST_IRQ_STATUS},
|
||
- {"unexpected_rst_irq_clear", S_IWUSR, TSPP2_UNEXPECTED_RST_IRQ_CLEAR},
|
||
- {"unexpected_rst_irq_en", TSPP2_S_RW, TSPP2_UNEXPECTED_RST_IRQ_ENABLE},
|
||
- {"wrong_pipe_dir_irq_status", S_IRUGO, TSPP2_WRONG_PIPE_DIR_IRQ_STATUS},
|
||
- {"wrong_pipe_dir_irq_clear", S_IWUSR, TSPP2_WRONG_PIPE_DIR_IRQ_CLEAR},
|
||
- {"wrong_pipe_dir_irq_en", TSPP2_S_RW, TSPP2_WRONG_PIPE_DIR_IRQ_ENABLE},
|
||
- {"qsb_response_error_irq_status", S_IRUGO,
|
||
- TSPP2_QSB_RESPONSE_ERROR_IRQ_STATUS},
|
||
- {"qsb_response_error_irq_clear", S_IWUSR,
|
||
- TSPP2_QSB_RESPONSE_ERROR_IRQ_CLEAR},
|
||
- {"qsb_response_error_irq_en", TSPP2_S_RW,
|
||
- TSPP2_QSB_RESPONSE_ERROR_IRQ_ENABLE},
|
||
- {"src_total_tsp_reset", S_IWUSR, TSPP2_SRC_TOTAL_TSP_RESET},
|
||
- {"src_filtered_out_tsp_reset", S_IWUSR,
|
||
- TSPP2_SRC_FILTERED_OUT_TSP_RESET},
|
||
- {"data_not_sent_on_pipe_reset", S_IWUSR,
|
||
- TSPP2_DATA_NOT_SENT_ON_PIPE_RESET},
|
||
- {"version", S_IRUGO, TSPP2_VERSION},
|
||
- /* Scrambling bits monitoring interrupt registers */
|
||
- {"sc_go_high_status_0", S_IRUGO, TSPP2_SC_GO_HIGH_STATUS(0)},
|
||
- {"sc_go_high_status_1", S_IRUGO, TSPP2_SC_GO_HIGH_STATUS(1)},
|
||
- {"sc_go_high_status_2", S_IRUGO, TSPP2_SC_GO_HIGH_STATUS(2)},
|
||
- {"sc_go_high_status_3", S_IRUGO, TSPP2_SC_GO_HIGH_STATUS(3)},
|
||
- {"sc_go_high_clear_0", S_IWUSR, TSPP2_SC_GO_HIGH_CLEAR(0)},
|
||
- {"sc_go_high_clear_1", S_IWUSR, TSPP2_SC_GO_HIGH_CLEAR(1)},
|
||
- {"sc_go_high_clear_2", S_IWUSR, TSPP2_SC_GO_HIGH_CLEAR(2)},
|
||
- {"sc_go_high_clear_3", S_IWUSR, TSPP2_SC_GO_HIGH_CLEAR(3)},
|
||
- {"sc_go_high_en_0", TSPP2_S_RW, TSPP2_SC_GO_HIGH_ENABLE(0)},
|
||
- {"sc_go_high_en_1", TSPP2_S_RW, TSPP2_SC_GO_HIGH_ENABLE(1)},
|
||
- {"sc_go_high_en_2", TSPP2_S_RW, TSPP2_SC_GO_HIGH_ENABLE(2)},
|
||
- {"sc_go_high_en_3", TSPP2_S_RW, TSPP2_SC_GO_HIGH_ENABLE(3)},
|
||
- {"sc_go_low_status_0", S_IRUGO, TSPP2_SC_GO_LOW_STATUS(0)},
|
||
- {"sc_go_low_status_1", S_IRUGO, TSPP2_SC_GO_LOW_STATUS(1)},
|
||
- {"sc_go_low_status_2", S_IRUGO, TSPP2_SC_GO_LOW_STATUS(2)},
|
||
- {"sc_go_low_status_3", S_IRUGO, TSPP2_SC_GO_LOW_STATUS(3)},
|
||
- {"sc_go_low_clear_0", S_IWUSR, TSPP2_SC_GO_LOW_CLEAR(0)},
|
||
- {"sc_go_low_clear_1", S_IWUSR, TSPP2_SC_GO_LOW_CLEAR(1)},
|
||
- {"sc_go_low_clear_2", S_IWUSR, TSPP2_SC_GO_LOW_CLEAR(2)},
|
||
- {"sc_go_low_clear_3", S_IWUSR, TSPP2_SC_GO_LOW_CLEAR(3)},
|
||
- {"sc_go_low_en_0", TSPP2_S_RW, TSPP2_SC_GO_LOW_ENABLE(0)},
|
||
- {"sc_go_low_en_1", TSPP2_S_RW, TSPP2_SC_GO_LOW_ENABLE(1)},
|
||
- {"sc_go_low_en_2", TSPP2_S_RW, TSPP2_SC_GO_LOW_ENABLE(2)},
|
||
- {"sc_go_low_en_3", TSPP2_S_RW, TSPP2_SC_GO_LOW_ENABLE(3)},
|
||
-};
|
||
-
|
||
-/* Data structures */
|
||
-
|
||
-/**
|
||
- * struct tspp2_tsif_device - TSIF device
|
||
- *
|
||
- * @base: TSIF device memory base address.
|
||
- * @hw_index: TSIF device HW index (0 .. (TSPP2_NUM_TSIF_INPUTS - 1)).
|
||
- * @dev: Back pointer to the TSPP2 device.
|
||
- * @time_limit: TSIF device time limit
|
||
- * (maximum time allowed between each TS packet).
|
||
- * @ref_count: TSIF device reference count.
|
||
- * @tsif_irq: TSIF device IRQ number.
|
||
- * @mode: TSIF mode of operation.
|
||
- * @clock_inverse: Invert input clock signal.
|
||
- * @data_inverse: Invert input data signal.
|
||
- * @sync_inverse: Invert input sync signal.
|
||
- * @enable_inverse: Invert input enable signal.
|
||
- * @debugfs_entrys: TSIF device debugfs entry.
|
||
- * @stat_pkt_write_err: TSIF device packet write error statistics.
|
||
- * @stat__pkt_read_err: TSIF device packet read error statistics.
|
||
- * @stat_overflow: TSIF device overflow statistics.
|
||
- * @stat_lost_sync: TSIF device lost sync statistics.
|
||
- * @stat_timeout: TSIF device timeout statistics.
|
||
- */
|
||
-struct tspp2_tsif_device {
|
||
- void __iomem *base;
|
||
- u32 hw_index;
|
||
- struct tspp2_device *dev;
|
||
- u32 time_limit;
|
||
- u32 ref_count;
|
||
- u32 tsif_irq;
|
||
- enum tspp2_tsif_mode mode;
|
||
- int clock_inverse;
|
||
- int data_inverse;
|
||
- int sync_inverse;
|
||
- int enable_inverse;
|
||
- struct dentry *debugfs_entry;
|
||
- u32 stat_pkt_write_err;
|
||
- u32 stat_pkt_read_err;
|
||
- u32 stat_overflow;
|
||
- u32 stat_lost_sync;
|
||
- u32 stat_timeout;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_indexing_table - Indexing table
|
||
- *
|
||
- * @prefix_value: 4-byte common prefix value.
|
||
- * @prefix_mask: 4-byte prefix mask.
|
||
- * @entry_value: An array of 4-byte pattern values.
|
||
- * @entry_mask: An array of corresponding 4-byte pattern masks.
|
||
- * @num_valid_entries: Number of valid entries in the arrays.
|
||
- */
|
||
-struct tspp2_indexing_table {
|
||
- u32 prefix_value;
|
||
- u32 prefix_mask;
|
||
- u32 entry_value[TSPP2_NUM_INDEXING_PATTERNS];
|
||
- u32 entry_mask[TSPP2_NUM_INDEXING_PATTERNS];
|
||
- u16 num_valid_entries;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_event_work - Event work information
|
||
- *
|
||
- * @device: TSPP2 device back-pointer.
|
||
- * @callback: Callback to invoke.
|
||
- * @cookie: Cookie to pass to the callback.
|
||
- * @event_bitmask: A bit mask of events to pass to the callback.
|
||
- * @work: The work structure to queue.
|
||
- * @link: A list element.
|
||
- */
|
||
-struct tspp2_event_work {
|
||
- struct tspp2_device *device;
|
||
- void (*callback)(void *cookie, u32 event_bitmask);
|
||
- void *cookie;
|
||
- u32 event_bitmask;
|
||
- struct work_struct work;
|
||
- struct list_head link;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_filter - Filter object
|
||
- *
|
||
- * @opened: A flag to indicate whether the filter is open.
|
||
- * @device: Back-pointer to the TSPP2 device the filter
|
||
- * belongs to.
|
||
- * @batch: The filter batch this filter belongs to.
|
||
- * @src: Back-pointer to the source the filter is
|
||
- * associated with.
|
||
- * @hw_index: The filter's HW index.
|
||
- * @pid_value: The filter's 13-bit PID value.
|
||
- * @mask: The corresponding 13-bit bitmask.
|
||
- * @context: The filter's context ID.
|
||
- * @indexing_table_id: The ID of the indexing table this filter uses
|
||
- * in case an indexing operation is set.
|
||
- * @operations: An array of user-defined operations.
|
||
- * @num_user_operations: The number of user-defined operations.
|
||
- * @indexing_op_set: A flag to indicate an indexing operation
|
||
- * has been set.
|
||
- * @raw_op_with_indexing: A flag to indicate a Raw Transmit operation
|
||
- * with support_indexing parameter has been set.
|
||
- * @pes_analysis_op_set: A flag to indicate a PES Analysis operation
|
||
- * has been set.
|
||
- * @raw_op_set: A flag to indicate a Raw Transmit operation
|
||
- * has been set.
|
||
- * @pes_tx_op_set: A flag to indicate a PES Transmit operation
|
||
- * has been set.
|
||
- * @event_callback: A user callback to invoke when a filter event
|
||
- * occurs.
|
||
- * @event_cookie: A user cookie to provide to the callback.
|
||
- * @event_bitmask: A bit mask of filter events
|
||
- * TSPP2_FILTER_EVENT_XXX.
|
||
- * @enabled: A flag to indicate whether the filter
|
||
- * is enabled.
|
||
- * @link: A list element. When the filter is associated
|
||
- * with a source, it is added to the source's
|
||
- * list of filters.
|
||
- */
|
||
-struct tspp2_filter {
|
||
- int opened;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_filter_batch *batch;
|
||
- struct tspp2_src *src;
|
||
- u16 hw_index;
|
||
- u16 pid_value;
|
||
- u16 mask;
|
||
- u16 context;
|
||
- u8 indexing_table_id;
|
||
- struct tspp2_operation operations[TSPP2_MAX_OPS_PER_FILTER];
|
||
- u8 num_user_operations;
|
||
- int indexing_op_set;
|
||
- int raw_op_with_indexing;
|
||
- int pes_analysis_op_set;
|
||
- int raw_op_set;
|
||
- int pes_tx_op_set;
|
||
- void (*event_callback)(void *cookie, u32 event_bitmask);
|
||
- void *event_cookie;
|
||
- u32 event_bitmask;
|
||
- int enabled;
|
||
- struct list_head link;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_pipe - Pipe object
|
||
- *
|
||
- * @opened: A flag to indicate whether the pipe is open.
|
||
- * @device: Back-pointer to the TSPP2 device the pipe belongs to.
|
||
- * @cfg: Pipe configuration parameters.
|
||
- * @sps_pipe: The BAM SPS pipe.
|
||
- * @sps_connect_cfg: SPS pipe connection configuration.
|
||
- * @sps_event: SPS pipe event registration parameters.
|
||
- * @desc_ion_handle: ION handle for the SPS pipe descriptors.
|
||
- * @iova: TSPP2 IOMMU-mapped virtual address of the
|
||
- * data buffer provided by the user.
|
||
- * @hw_index: The pipe's HW index (for register access).
|
||
- * @threshold: Pipe threshold.
|
||
- * @ref_cnt: Pipe reference count. Incremented when pipe
|
||
- * is attached to a source, decremented when it
|
||
- * is detached from a source.
|
||
- */
|
||
-struct tspp2_pipe {
|
||
- int opened;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_pipe_config_params cfg;
|
||
- struct sps_pipe *sps_pipe;
|
||
- struct sps_connect sps_connect_cfg;
|
||
- struct sps_register_event sps_event;
|
||
- struct ion_handle *desc_ion_handle;
|
||
- ion_phys_addr_t iova;
|
||
- u32 hw_index;
|
||
- u16 threshold;
|
||
- u32 ref_cnt;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_output_pipe - Output pipe element to add to a source's list
|
||
- *
|
||
- * @pipe: A pointer to an output pipe object.
|
||
- * @link: A list element. When an output pipe is attached to a source,
|
||
- * it is added to the source's output pipe list. Note the same pipe
|
||
- * can be attached to multiple sources, so we allocate an output
|
||
- * pipe element to add to the list - we don't add the actual pipe.
|
||
- */
|
||
-struct tspp2_output_pipe {
|
||
- struct tspp2_pipe *pipe;
|
||
- struct list_head link;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_filter_batch - Filter batch object
|
||
- *
|
||
- * @batch_id: Filter batch ID.
|
||
- * @hw_filters: An array of HW filters that belong to this batch. When set, this
|
||
- * indicates the filter is used. The actual HW index of a filter is
|
||
- * calculated according to the index in this array along with the
|
||
- * batch ID.
|
||
- * @src: Back-pointer to the source the batch is associated with. This is
|
||
- * also used to indicate this batch is "taken".
|
||
- * @link: A list element. When the batch is associated with a source, it
|
||
- * is added to the source's list of filter batches.
|
||
- */
|
||
-struct tspp2_filter_batch {
|
||
- u8 batch_id;
|
||
- int hw_filters[TSPP2_FILTERS_PER_BATCH];
|
||
- struct tspp2_src *src;
|
||
- struct list_head link;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_src - Source object
|
||
- *
|
||
- * @opened: A flag to indicate whether the source is open.
|
||
- * @device: Back-pointer to the TSPP2 device the source
|
||
- * belongs to.
|
||
- * @hw_index: The source's HW index. This is used when writing
|
||
- * to HW registers relevant for this source.
|
||
- * There are registers specific to TSIF or memory
|
||
- * sources, and there are registers common to all
|
||
- * sources.
|
||
- * @input: Source input type (TSIF / memory).
|
||
- * @pkt_format: Input packet size and format for this source.
|
||
- * @scrambling_bits_monitoring: Scrambling bits monitoring mode.
|
||
- * @batches_list: A list of associated filter batches.
|
||
- * @filters_list: A list of associated filters.
|
||
- * @input_pipe: A pointer to the source's input pipe, if exists.
|
||
- * @output_pipe_list: A list of output pipes attached to the source.
|
||
- * For each pipe we also save whether it is
|
||
- * stalling for this source.
|
||
- * @num_associated_batches: Number of associated filter batches.
|
||
- * @num_associated_pipes: Number of associated pipes.
|
||
- * @num_associated_filters: Number of associated filters.
|
||
- * @reserved_filter_hw_index: A HW filter index reserved for updating an
|
||
- * active filter's operations.
|
||
- * @event_callback: A user callback to invoke when a source event
|
||
- * occurs.
|
||
- * @event_cookie: A user cookie to provide to the callback.
|
||
- * @event_bitmask: A bit mask of source events
|
||
- * TSPP2_SRC_EVENT_XXX.
|
||
- * @enabled: A flag to indicate whether the source
|
||
- * is enabled.
|
||
- */
|
||
-struct tspp2_src {
|
||
- int opened;
|
||
- struct tspp2_device *device;
|
||
- u8 hw_index;
|
||
- enum tspp2_src_input input;
|
||
- enum tspp2_packet_format pkt_format;
|
||
- enum tspp2_src_scrambling_monitoring scrambling_bits_monitoring;
|
||
- struct list_head batches_list;
|
||
- struct list_head filters_list;
|
||
- struct tspp2_pipe *input_pipe;
|
||
- struct list_head output_pipe_list;
|
||
- u8 num_associated_batches;
|
||
- u8 num_associated_pipes;
|
||
- u32 num_associated_filters;
|
||
- u16 reserved_filter_hw_index;
|
||
- void (*event_callback)(void *cookie, u32 event_bitmask);
|
||
- void *event_cookie;
|
||
- u32 event_bitmask;
|
||
- int enabled;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_global_irq_stats - Global interrupt statistics counters
|
||
- *
|
||
- * @tsp_invalid_af_control: Invalid adaptation field control bit.
|
||
- * @tsp_invalid_length: Invalid adaptation field length.
|
||
- * @pes_no_sync: PES sync sequence not found.
|
||
- * @encrypt_level_err: Cipher operation configuration error.
|
||
- */
|
||
-struct tspp2_global_irq_stats {
|
||
- u32 tsp_invalid_af_control;
|
||
- u32 tsp_invalid_length;
|
||
- u32 pes_no_sync;
|
||
- u32 encrypt_level_err;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_src_irq_stats - Memory source interrupt statistics counters
|
||
- *
|
||
- * @read_failure: Failure to read from memory input.
|
||
- * @flow_control_stall: Input is stalled due to flow control.
|
||
- */
|
||
-struct tspp2_src_irq_stats {
|
||
- u32 read_failure;
|
||
- u32 flow_control_stall;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_keytable_irq_stats - Key table interrupt statistics counters
|
||
- *
|
||
- * @key_not_ready: Ciphering keys are not ready in the key table.
|
||
- */
|
||
-struct tspp2_keytable_irq_stats {
|
||
- u32 key_not_ready;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_pipe_irq_stats - Pipe interrupt statistics counters
|
||
- *
|
||
- * @unexpected_reset: SW reset the pipe before all operations on this
|
||
- * pipe ended.
|
||
- * @qsb_response_error: TX operation ends with QSB error.
|
||
- * @wrong_pipe_direction: Trying to use a pipe in the wrong direction.
|
||
- */
|
||
-struct tspp2_pipe_irq_stats {
|
||
- u32 unexpected_reset;
|
||
- u32 qsb_response_error;
|
||
- u32 wrong_pipe_direction;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_filter_context_irq_stats - Filter interrupt statistics counters
|
||
- *
|
||
- * @sc_go_high: Scrambling bits change from clear to encrypted.
|
||
- * @sc_go_low: Scrambling bits change from encrypted to clear.
|
||
- */
|
||
-struct tspp2_filter_context_irq_stats {
|
||
- u32 sc_go_high;
|
||
- u32 sc_go_low;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_irq_stats - Interrupt statistics counters
|
||
- *
|
||
- * @global: Global interrupt statistics counters
|
||
- * @src: Memory source interrupt statistics counters
|
||
- * @kt: Key table interrupt statistics counters
|
||
- * @pipe: Pipe interrupt statistics counters
|
||
- * @ctx: Filter context interrupt statistics counters
|
||
- */
|
||
-struct tspp2_irq_stats {
|
||
- struct tspp2_global_irq_stats global;
|
||
- struct tspp2_src_irq_stats src[TSPP2_NUM_MEM_INPUTS];
|
||
- struct tspp2_keytable_irq_stats kt[TSPP2_NUM_KEYTABLES];
|
||
- struct tspp2_pipe_irq_stats pipe[TSPP2_NUM_PIPES];
|
||
- struct tspp2_filter_context_irq_stats ctx[TSPP2_NUM_CONTEXTS];
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_iommu_info - TSPP2 IOMMU information
|
||
- *
|
||
- * @hlos_group: TSPP2 IOMMU HLOS (Non-Secure) group.
|
||
- * @cpz_group: TSPP2 IOMMU HLOS (Secure) group.
|
||
- * @hlos_domain: TSPP2 IOMMU HLOS (Non-Secure) domain.
|
||
- * @cpz_domain: TSPP2 IOMMU CPZ (Secure) domain.
|
||
- * @hlos_domain_num: TSPP2 IOMMU HLOS (Non-Secure) domain number.
|
||
- * @cpz_domain_num: TSPP2 IOMMU CPZ (Secure) domain number.
|
||
- * @hlos_partition: TSPP2 IOMMU HLOS partition number.
|
||
- * @cpz_partition: TSPP2 IOMMU CPZ partition number.
|
||
- */
|
||
-struct tspp2_iommu_info {
|
||
- struct iommu_group *hlos_group;
|
||
- struct iommu_group *cpz_group;
|
||
- struct iommu_domain *hlos_domain;
|
||
- struct iommu_domain *cpz_domain;
|
||
- int hlos_domain_num;
|
||
- int cpz_domain_num;
|
||
- int hlos_partition;
|
||
- int cpz_partition;
|
||
-};
|
||
-
|
||
-/**
|
||
- * struct tspp2_device - TSPP2 device
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @opened: A flag to indicate whether the device is open.
|
||
- * @pdev: Platform device.
|
||
- * @dev: Device structure, used for driver prints.
|
||
- * @base: TSPP2 Device memory base address.
|
||
- * @tspp2_irq: TSPP2 Device IRQ number.
|
||
- * @bam_handle: BAM handle.
|
||
- * @bam_irq: BAM IRQ number.
|
||
- * @bam_props: BAM properties.
|
||
- * @iommu_info: IOMMU information.
|
||
- * @wakeup_src: A wakeup source to keep CPU awake when needed.
|
||
- * @spinlock: A spinlock to protect accesses to
|
||
- * data structures that happen from APIs and ISRs.
|
||
- * @mutex: A mutex for mutual exclusion between API calls.
|
||
- * @tsif_devices: An array of TSIF devices.
|
||
- * @gdsc: GDSC power regulator.
|
||
- * @bus_client: Client for bus bandwidth voting.
|
||
- * @tspp2_ahb_clk: TSPP2 AHB clock.
|
||
- * @tspp2_core_clk: TSPP2 core clock.
|
||
- * @tspp2_vbif_clk: TSPP2 VBIF clock.
|
||
- * @vbif_ahb_clk: VBIF AHB clock.
|
||
- * @vbif_axi_clk: VBIF AXI clock.
|
||
- * @tspp2_klm_ahb_clk: TSPP2 KLM AHB clock.
|
||
- * @tsif_ref_clk: TSIF reference clock.
|
||
- * @batches: An array of filter batch objects.
|
||
- * @contexts: An array of context indexes. The index in this
|
||
- * array represents the context's HW index, while
|
||
- * the value represents whether it is used by a
|
||
- * filter or free.
|
||
- * @indexing_tables: An array of indexing tables.
|
||
- * @tsif_sources: An array of source objects for TSIF input.
|
||
- * @mem_sources: An array of source objects for memory input.
|
||
- * @filters: An array of filter objects.
|
||
- * @pipes: An array of pipe objects.
|
||
- * @num_secured_opened_pipes: Number of secured opened pipes.
|
||
- * @num_non_secured_opened_pipes: Number of non-secured opened pipes.
|
||
- * @num_enabled_sources: Number of enabled sources.
|
||
- * @work_queue: A work queue for invoking user callbacks.
|
||
- * @event_callback: A user callback to invoke when a global event
|
||
- * occurs.
|
||
- * @event_cookie: A user cookie to provide to the callback.
|
||
- * @event_bitmask: A bit mask of global events
|
||
- * TSPP2_GLOBAL_EVENT_XXX.
|
||
- * @debugfs_entry: TSPP2 device debugfs entry.
|
||
- * @irq_stats: TSPP2 IRQ statistics.
|
||
- * @free_work_list: A list of available work elements.
|
||
- * @work_pool: A pool of work elements.
|
||
- */
|
||
-struct tspp2_device {
|
||
- u32 dev_id;
|
||
- int opened;
|
||
- struct platform_device *pdev;
|
||
- struct device *dev;
|
||
- void __iomem *base;
|
||
- u32 tspp2_irq;
|
||
- unsigned long bam_handle;
|
||
- u32 bam_irq;
|
||
- struct sps_bam_props bam_props;
|
||
- struct tspp2_iommu_info iommu_info;
|
||
- struct wakeup_source wakeup_src;
|
||
- spinlock_t spinlock;
|
||
- struct mutex mutex;
|
||
- struct tspp2_tsif_device tsif_devices[TSPP2_NUM_TSIF_INPUTS];
|
||
- struct regulator *gdsc;
|
||
- uint32_t bus_client;
|
||
- struct clk *tspp2_ahb_clk;
|
||
- struct clk *tspp2_core_clk;
|
||
- struct clk *tspp2_vbif_clk;
|
||
- struct clk *vbif_ahb_clk;
|
||
- struct clk *vbif_axi_clk;
|
||
- struct clk *tspp2_klm_ahb_clk;
|
||
- struct clk *tsif_ref_clk;
|
||
- struct tspp2_filter_batch batches[TSPP2_NUM_BATCHES];
|
||
- int contexts[TSPP2_NUM_AVAIL_CONTEXTS];
|
||
- struct tspp2_indexing_table indexing_tables[TSPP2_NUM_INDEXING_TABLES];
|
||
- struct tspp2_src tsif_sources[TSPP2_NUM_TSIF_INPUTS];
|
||
- struct tspp2_src mem_sources[TSPP2_NUM_MEM_INPUTS];
|
||
- struct tspp2_filter filters[TSPP2_NUM_AVAIL_FILTERS];
|
||
- struct tspp2_pipe pipes[TSPP2_NUM_PIPES];
|
||
- u8 num_secured_opened_pipes;
|
||
- u8 num_non_secured_opened_pipes;
|
||
- u8 num_enabled_sources;
|
||
- struct workqueue_struct *work_queue;
|
||
- void (*event_callback)(void *cookie, u32 event_bitmask);
|
||
- void *event_cookie;
|
||
- u32 event_bitmask;
|
||
- struct dentry *debugfs_entry;
|
||
- struct tspp2_irq_stats irq_stats;
|
||
- struct list_head free_work_list;
|
||
- struct tspp2_event_work work_pool[TSPP2_NUM_EVENT_WORK_ELEMENTS];
|
||
-};
|
||
-
|
||
-/* Global TSPP2 devices database */
|
||
-static struct tspp2_device *tspp2_devices[TSPP2_NUM_DEVICES];
|
||
-
|
||
-/* debugfs support */
|
||
-
|
||
-static int debugfs_iomem_x32_set(void *data, u64 val)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_device *device = tspp2_devices[0]; /* Assuming device 0 */
|
||
-
|
||
- if (!device->opened)
|
||
- return -ENODEV;
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- writel_relaxed(val, data);
|
||
- wmb();
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-static int debugfs_iomem_x32_get(void *data, u64 *val)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_device *device = tspp2_devices[0]; /* Assuming device 0 */
|
||
-
|
||
- if (!device->opened)
|
||
- return -ENODEV;
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- *val = readl_relaxed(data);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
|
||
- debugfs_iomem_x32_set, "0x%08llX");
|
||
-
|
||
-static int debugfs_dev_open_set(void *data, u64 val)
|
||
-{
|
||
- int ret = 0;
|
||
-
|
||
- /* Assuming device 0 */
|
||
- if (val == 1)
|
||
- ret = tspp2_device_open(0);
|
||
- else
|
||
- ret = tspp2_device_close(0);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-static int debugfs_dev_open_get(void *data, u64 *val)
|
||
-{
|
||
- struct tspp2_device *device = tspp2_devices[0]; /* Assuming device 0 */
|
||
-
|
||
- *val = device->opened;
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-DEFINE_SIMPLE_ATTRIBUTE(fops_device_open, debugfs_dev_open_get,
|
||
- debugfs_dev_open_set, "0x%08llX");
|
||
-
|
||
-/**
|
||
- * tspp2_tsif_debugfs_init() - TSIF device debugfs initialization.
|
||
- *
|
||
- * @tsif_device: TSIF device.
|
||
- */
|
||
-static void tspp2_tsif_debugfs_init(struct tspp2_tsif_device *tsif_device)
|
||
-{
|
||
- int i;
|
||
- char name[10];
|
||
- struct dentry *dentry;
|
||
- void __iomem *base = tsif_device->base;
|
||
-
|
||
- snprintf(name, 10, "tsif%i", tsif_device->hw_index);
|
||
- tsif_device->debugfs_entry = debugfs_create_dir(name, NULL);
|
||
-
|
||
- if (!tsif_device->debugfs_entry)
|
||
- return;
|
||
-
|
||
- dentry = tsif_device->debugfs_entry;
|
||
- if (dentry) {
|
||
- for (i = 0; i < ARRAY_SIZE(tsif_regs); i++) {
|
||
- debugfs_create_file(
|
||
- tsif_regs[i].name,
|
||
- tsif_regs[i].mode,
|
||
- dentry,
|
||
- base + tsif_regs[i].offset,
|
||
- &fops_iomem_x32);
|
||
- }
|
||
- }
|
||
-
|
||
- dentry = debugfs_create_dir("statistics", tsif_device->debugfs_entry);
|
||
- if (dentry) {
|
||
- debugfs_create_u32(
|
||
- "stat_pkt_write_err",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &tsif_device->stat_pkt_write_err);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_pkt_read_err",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &tsif_device->stat_pkt_read_err);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_overflow",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &tsif_device->stat_overflow);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_lost_sync",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &tsif_device->stat_lost_sync);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_timeout",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &tsif_device->stat_timeout);
|
||
- }
|
||
-}
|
||
-
|
||
-static char *op_to_string(enum tspp2_operation_type op)
|
||
-{
|
||
- switch (op) {
|
||
- case TSPP2_OP_PES_ANALYSIS:
|
||
- return "TSPP2_OP_PES_ANALYSIS";
|
||
- case TSPP2_OP_RAW_TRANSMIT:
|
||
- return "TSPP2_OP_RAW_TRANSMIT";
|
||
- case TSPP2_OP_PES_TRANSMIT:
|
||
- return "TSPP2_OP_PES_TRANSMIT";
|
||
- case TSPP2_OP_PCR_EXTRACTION:
|
||
- return "TSPP2_OP_PCR_EXTRACTION";
|
||
- case TSPP2_OP_CIPHER:
|
||
- return "TSPP2_OP_CIPHER";
|
||
- case TSPP2_OP_INDEXING:
|
||
- return "TSPP2_OP_INDEXING";
|
||
- case TSPP2_OP_COPY_PACKET:
|
||
- return "TSPP2_OP_COPY_PACKET";
|
||
- default:
|
||
- return "Invalid Operation";
|
||
- }
|
||
-}
|
||
-
|
||
-static char *src_input_to_string(enum tspp2_src_input src_input)
|
||
-{
|
||
- switch (src_input) {
|
||
- case TSPP2_INPUT_TSIF0:
|
||
- return "TSPP2_INPUT_TSIF0";
|
||
- case TSPP2_INPUT_TSIF1:
|
||
- return "TSPP2_INPUT_TSIF1";
|
||
- case TSPP2_INPUT_MEMORY:
|
||
- return "TSPP2_INPUT_MEMORY";
|
||
- default:
|
||
- return "Unknown source input type";
|
||
- }
|
||
-}
|
||
-
|
||
-static char *pkt_format_to_string(enum tspp2_packet_format pkt_format)
|
||
-{
|
||
- switch (pkt_format) {
|
||
- case TSPP2_PACKET_FORMAT_188_RAW:
|
||
- return "TSPP2_PACKET_FORMAT_188_RAW";
|
||
- case TSPP2_PACKET_FORMAT_192_HEAD:
|
||
- return "TSPP2_PACKET_FORMAT_192_HEAD";
|
||
- case TSPP2_PACKET_FORMAT_192_TAIL:
|
||
- return "TSPP2_PACKET_FORMAT_192_TAIL";
|
||
- default:
|
||
- return "Unknown packet format";
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * debugfs service to print device information.
|
||
- */
|
||
-static int tspp2_device_debugfs_print(struct seq_file *s, void *p)
|
||
-{
|
||
- int count;
|
||
- int exist_flag = 0;
|
||
- struct tspp2_device *device = (struct tspp2_device *)s->private;
|
||
-
|
||
- seq_printf(s, "dev_id: %d\n", device->dev_id);
|
||
- seq_puts(s, "Enabled filters:");
|
||
- for (count = 0; count < TSPP2_NUM_AVAIL_FILTERS; count++)
|
||
- if (device->filters[count].enabled) {
|
||
- seq_printf(s, "\n\tfilter%3d", count);
|
||
- exist_flag = 1;
|
||
- }
|
||
- if (!exist_flag)
|
||
- seq_puts(s, " none\n");
|
||
- else
|
||
- seq_puts(s, "\n");
|
||
-
|
||
- exist_flag = 0;
|
||
- seq_puts(s, "Opened filters:");
|
||
- for (count = 0; count < TSPP2_NUM_AVAIL_FILTERS; count++)
|
||
- if (device->filters[count].opened) {
|
||
- seq_printf(s, "\n\tfilter%3d", count);
|
||
- exist_flag = 1;
|
||
- }
|
||
- if (!exist_flag)
|
||
- seq_puts(s, " none\n");
|
||
- else
|
||
- seq_puts(s, "\n");
|
||
-
|
||
- exist_flag = 0;
|
||
- seq_puts(s, "Opened pipes:\n");
|
||
- for (count = 0; count < TSPP2_NUM_PIPES; count++)
|
||
- if (device->pipes[count].opened) {
|
||
- seq_printf(s, "\tpipe%2d\n", count);
|
||
- exist_flag = 1;
|
||
- }
|
||
- if (!exist_flag)
|
||
- seq_puts(s, " none\n");
|
||
- else
|
||
- seq_puts(s, "\n");
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * debugfs service to print source information.
|
||
- */
|
||
-static int tspp2_src_debugfs_print(struct seq_file *s, void *p)
|
||
-{
|
||
- struct tspp2_filter_batch *batch;
|
||
- struct tspp2_filter *filter;
|
||
- struct tspp2_output_pipe *output_pipe;
|
||
- struct tspp2_src *src = (struct tspp2_src *)s->private;
|
||
-
|
||
- if (!src) {
|
||
- seq_puts(s, "error\n");
|
||
- return 1;
|
||
- }
|
||
- seq_printf(s, "Status: %s\n", src->enabled ? "enabled" : "disabled");
|
||
- seq_printf(s, "hw_index: %d\n", src->hw_index);
|
||
- seq_printf(s, "event_bitmask: 0x%08X\n", src->event_bitmask);
|
||
- if (src->input_pipe)
|
||
- seq_printf(s, "input_pipe hw_index: %d\n",
|
||
- src->input_pipe->hw_index);
|
||
- seq_printf(s, "tspp2_src_input: %s\n", src_input_to_string(src->input));
|
||
- seq_printf(s, "pkt_format: %s\n",
|
||
- pkt_format_to_string(src->pkt_format));
|
||
- seq_printf(s, "num_associated_batches: %d\n",
|
||
- src->num_associated_batches);
|
||
-
|
||
- if (src->num_associated_batches) {
|
||
- seq_puts(s, "batch_ids: ");
|
||
- list_for_each_entry(batch, &src->batches_list, link)
|
||
- seq_printf(s, "%d ", batch->batch_id);
|
||
- seq_puts(s, "\n");
|
||
- }
|
||
-
|
||
- seq_printf(s, "num_associated_pipes: %d\n", src->num_associated_pipes);
|
||
- if (src->num_associated_pipes) {
|
||
- seq_puts(s, "pipes_hw_idxs: ");
|
||
- list_for_each_entry(output_pipe, &src->output_pipe_list, link) {
|
||
- seq_printf(s, "%d ", output_pipe->pipe->hw_index);
|
||
- }
|
||
- seq_puts(s, "\n");
|
||
- }
|
||
-
|
||
- seq_printf(s, "reserved_filter_hw_index: %d\n",
|
||
- src->reserved_filter_hw_index);
|
||
-
|
||
- seq_printf(s, "num_associated_filters: %d\n",
|
||
- src->num_associated_filters);
|
||
- if (src->num_associated_filters) {
|
||
- int i;
|
||
- seq_puts(s, "Open filters:\n");
|
||
- list_for_each_entry(filter, &src->filters_list, link) {
|
||
- if (!filter->opened)
|
||
- continue;
|
||
- seq_printf(s, "\thw_index: %d\n",
|
||
- filter->hw_index);
|
||
- seq_printf(s, "\tStatus: %s\n",
|
||
- filter->enabled ? "enabled"
|
||
- : "disabled");
|
||
- seq_printf(s, "\tpid_value: 0x%08X\n",
|
||
- filter->pid_value);
|
||
- seq_printf(s, "\tmask: 0x%08X\n", filter->mask);
|
||
- seq_printf(s, "\tnum_user_operations: %d\n",
|
||
- filter->num_user_operations);
|
||
- if (filter->num_user_operations) {
|
||
- seq_puts(
|
||
- s, "\tTypes of operations:\n");
|
||
- for (i = 0;
|
||
- i < filter->num_user_operations; i++) {
|
||
- seq_printf(s, "\t\t%s\n", op_to_string(
|
||
- filter->operations[i].type));
|
||
- }
|
||
- }
|
||
- }
|
||
-
|
||
- } else {
|
||
- seq_puts(s, "no filters\n");
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * debugfs service to print filter information.
|
||
- */
|
||
-static int filter_debugfs_print(struct seq_file *s, void *p)
|
||
-{
|
||
- int i;
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)s->private;
|
||
-
|
||
- seq_printf(s, "Status: %s\n", filter->opened ? "opened" : "closed");
|
||
- if (filter->batch)
|
||
- seq_printf(s, "Located in batch %d\n", filter->batch->batch_id);
|
||
- if (filter->src)
|
||
- seq_printf(s, "Associated with src %d\n",
|
||
- filter->src->hw_index);
|
||
- seq_printf(s, "hw_index: %d\n", filter->hw_index);
|
||
- seq_printf(s, "pid_value: 0x%08X\n", filter->pid_value);
|
||
- seq_printf(s, "mask: 0x%08X\n", filter->mask);
|
||
- seq_printf(s, "context: %d\n", filter->context);
|
||
- seq_printf(s, "indexing_table_id: %d\n", filter->indexing_table_id);
|
||
- seq_printf(s, "num_user_operations: %d\n", filter->num_user_operations);
|
||
- seq_puts(s, "Types of operations:\n");
|
||
- for (i = 0; i < filter->num_user_operations; i++)
|
||
- seq_printf(s, "\t%s\n", op_to_string(
|
||
- filter->operations[i].type));
|
||
- seq_printf(s, "indexing_op_set: %d\n", filter->indexing_op_set);
|
||
- seq_printf(s, "raw_op_with_indexing: %d\n",
|
||
- filter->raw_op_with_indexing);
|
||
- seq_printf(s, "pes_analysis_op_set: %d\n", filter->pes_analysis_op_set);
|
||
- seq_printf(s, "raw_op_set: %d\n", filter->raw_op_set);
|
||
- seq_printf(s, "pes_tx_op_set: %d\n", filter->pes_tx_op_set);
|
||
- seq_printf(s, "Status: %s\n", filter->enabled ? "enabled" : "disabled");
|
||
-
|
||
- if (filter->enabled) {
|
||
- seq_printf(s, "Filter context-based counters, context %d\n",
|
||
- filter->context);
|
||
- seq_printf(s, "filter_tsp_sync_err = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_TSP_SYNC_ERROR(filter->context)));
|
||
- seq_printf(s, "filter_erred_tsp = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_ERRED_TSP(filter->context)));
|
||
- seq_printf(s, "filter_discontinuities = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_DISCONTINUITIES(filter->context)));
|
||
- seq_printf(s, "filter_sc_bits_discard = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_SCRAMBLING_BITS_DISCARD(filter->context)));
|
||
- seq_printf(s, "filter_tsp_total_num = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_TSP_TOTAL_NUM(filter->context)));
|
||
- seq_printf(s, "filter_discont_indicator = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_DISCONT_INDICATOR(filter->context)));
|
||
- seq_printf(s, "filter_tsp_no_payload = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_TSP_NO_PAYLOAD(filter->context)));
|
||
- seq_printf(s, "filter_tsp_duplicate = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_TSP_DUPLICATE(filter->context)));
|
||
- seq_printf(s, "filter_key_fetch_fail = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_KEY_FETCH_FAILURE(filter->context)));
|
||
- seq_printf(s, "filter_dropped_pcr = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_DROPPED_PCR(filter->context)));
|
||
- seq_printf(s, "filter_pes_errors = 0x%08X\n",
|
||
- readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_PES_ERRORS(filter->context)));
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * debugfs service to print pipe information.
|
||
- */
|
||
-static int pipe_debugfs_print(struct seq_file *s, void *p)
|
||
-{
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)s->private;
|
||
- seq_printf(s, "hw_index: %d\n", pipe->hw_index);
|
||
- seq_printf(s, "iova: 0x%08X\n", pipe->iova);
|
||
- seq_printf(s, "threshold: %d\n", pipe->threshold);
|
||
- seq_printf(s, "Status: %s\n", pipe->opened ? "opened" : "closed");
|
||
- seq_printf(s, "ref_cnt: %d\n", pipe->ref_cnt);
|
||
- return 0;
|
||
-}
|
||
-
|
||
-static int tspp2_dev_dbgfs_open(struct inode *inode, struct file *file)
|
||
-{
|
||
- return single_open(file, tspp2_device_debugfs_print,
|
||
- inode->i_private);
|
||
-}
|
||
-
|
||
-static int tspp2_filter_dbgfs_open(struct inode *inode, struct file *file)
|
||
-{
|
||
- return single_open(file, filter_debugfs_print, inode->i_private);
|
||
-}
|
||
-
|
||
-static int tspp2_pipe_dbgfs_open(struct inode *inode, struct file *file)
|
||
-{
|
||
- return single_open(file, pipe_debugfs_print, inode->i_private);
|
||
-}
|
||
-
|
||
-static int tspp2_src_dbgfs_open(struct inode *inode, struct file *file)
|
||
-{
|
||
- return single_open(file, tspp2_src_debugfs_print, inode->i_private);
|
||
-}
|
||
-
|
||
-static const struct file_operations dbgfs_tspp2_device_fops = {
|
||
- .open = tspp2_dev_dbgfs_open,
|
||
- .read = seq_read,
|
||
- .llseek = seq_lseek,
|
||
- .release = single_release,
|
||
- .owner = THIS_MODULE,
|
||
-};
|
||
-
|
||
-static const struct file_operations dbgfs_filter_fops = {
|
||
- .open = tspp2_filter_dbgfs_open,
|
||
- .read = seq_read,
|
||
- .llseek = seq_lseek,
|
||
- .release = single_release,
|
||
- .owner = THIS_MODULE,
|
||
-};
|
||
-
|
||
-static const struct file_operations dbgfs_pipe_fops = {
|
||
- .open = tspp2_pipe_dbgfs_open,
|
||
- .read = seq_read,
|
||
- .llseek = seq_lseek,
|
||
- .release = single_release,
|
||
- .owner = THIS_MODULE,
|
||
-};
|
||
-
|
||
-static const struct file_operations dbgfs_src_fops = {
|
||
- .open = tspp2_src_dbgfs_open,
|
||
- .read = seq_read,
|
||
- .llseek = seq_lseek,
|
||
- .release = single_release,
|
||
- .owner = THIS_MODULE,
|
||
-};
|
||
-
|
||
-/**
|
||
- * tspp2_tsif_debugfs_exit() - TSIF device debugfs teardown.
|
||
- *
|
||
- * @tsif_device: TSIF device.
|
||
- */
|
||
-static void tspp2_tsif_debugfs_exit(struct tspp2_tsif_device *tsif_device)
|
||
-{
|
||
- debugfs_remove_recursive(tsif_device->debugfs_entry);
|
||
- tsif_device->debugfs_entry = NULL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_debugfs_init() - TSPP2 device debugfs initialization.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- */
|
||
-static void tspp2_debugfs_init(struct tspp2_device *device)
|
||
-{
|
||
- int i, j;
|
||
- char name[80];
|
||
- struct dentry *dentry;
|
||
- struct dentry *dir;
|
||
- void __iomem *base = device->base;
|
||
-
|
||
- snprintf(name, 80, "tspp2_%i", device->dev_id);
|
||
- device->debugfs_entry = debugfs_create_dir(name, NULL);
|
||
-
|
||
- if (!device->debugfs_entry)
|
||
- return;
|
||
-
|
||
- /* Support device open/close */
|
||
- debugfs_create_file("open", TSPP2_S_RW, device->debugfs_entry,
|
||
- NULL, &fops_device_open);
|
||
-
|
||
- dentry = debugfs_create_dir("regs", device->debugfs_entry);
|
||
- if (dentry) {
|
||
- for (i = 0; i < ARRAY_SIZE(tspp2_regs); i++) {
|
||
- debugfs_create_file(
|
||
- tspp2_regs[i].name,
|
||
- tspp2_regs[i].mode,
|
||
- dentry,
|
||
- base + tspp2_regs[i].offset,
|
||
- &fops_iomem_x32);
|
||
- }
|
||
- }
|
||
-
|
||
- dentry = debugfs_create_dir("statistics", device->debugfs_entry);
|
||
- if (dentry) {
|
||
- debugfs_create_u32(
|
||
- "stat_tsp_invalid_af_control",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.global.tsp_invalid_af_control);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_tsp_invalid_length",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.global.tsp_invalid_length);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_pes_no_sync",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.global.pes_no_sync);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_encrypt_level_err",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.global.encrypt_level_err);
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("counters", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_CONTEXTS; i++) {
|
||
- snprintf(name, 80, "context%03i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_file("filter_tsp_sync_err",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_TSP_SYNC_ERROR(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_erred_tsp",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_ERRED_TSP(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_discontinuities",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_DISCONTINUITIES(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_sc_bits_discard",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_SCRAMBLING_BITS_DISCARD(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_tsp_total_num",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_TSP_TOTAL_NUM(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_discont_indicator",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_DISCONT_INDICATOR(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_tsp_no_payload",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_TSP_NO_PAYLOAD(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_tsp_duplicate",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_TSP_DUPLICATE(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_key_fetch_fail",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_KEY_FETCH_FAILURE(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_dropped_pcr",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_DROPPED_PCR(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_pes_errors",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_PES_ERRORS(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_sc_go_high",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.ctx[i].sc_go_high);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_sc_go_low",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.ctx[i].sc_go_low);
|
||
- }
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("filters", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_HW_FILTERS; i++) {
|
||
- snprintf(name, 80, "filter%03i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_file("filter_entry0",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_ENTRY0(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("filter_entry1",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_FILTER_ENTRY1(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- for (j = 0; j < TSPP2_MAX_OPS_PER_FILTER; j++) {
|
||
- snprintf(name, 80, "opcode%02i", j);
|
||
- debugfs_create_file(name,
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_OPCODE(i, j),
|
||
- &fops_iomem_x32);
|
||
- }
|
||
- }
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("mem_sources", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
|
||
- snprintf(name, 80, "mem_src%i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_u32(
|
||
- "stat_read_failure",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.src[i].read_failure);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_flow_control_stall",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.src[i].flow_control_stall);
|
||
- }
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("key_tables", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_KEYTABLES; i++) {
|
||
- snprintf(name, 80, "key_table%02i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_u32(
|
||
- "stat_key_not_ready",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.kt[i].key_not_ready);
|
||
- }
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("pipes", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_PIPES; i++) {
|
||
- snprintf(name, 80, "pipe%02i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_file("threshold",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_PIPE_THRESH_CONFIG(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("last_address",
|
||
- S_IRUGO,
|
||
- dentry,
|
||
- base + TSPP2_PIPE_LAST_ADDRESS(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("data_not_sent",
|
||
- S_IRUGO,
|
||
- dentry,
|
||
- base + TSPP2_DATA_NOT_SENT_ON_PIPE(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_unexpected_reset",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.pipe[i].unexpected_reset);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_qsb_response_error",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.pipe[i].qsb_response_error);
|
||
-
|
||
- debugfs_create_u32(
|
||
- "stat_wrong_pipe_direction",
|
||
- S_IRUGO | S_IWUSR | S_IWGRP,
|
||
- dentry,
|
||
- &device->irq_stats.pipe[i].
|
||
- wrong_pipe_direction);
|
||
- }
|
||
- }
|
||
-
|
||
- dir = debugfs_create_dir("indexing_tables", device->debugfs_entry);
|
||
- for (i = 0; i < TSPP2_NUM_INDEXING_TABLES; i++) {
|
||
- snprintf(name, 80, "indexing_table%i", i);
|
||
- dentry = debugfs_create_dir(name, dir);
|
||
- if (dentry) {
|
||
- debugfs_create_file("prefix",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_INDEX_TABLE_PREFIX(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("mask",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_INDEX_TABLE_PREFIX_MASK(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- debugfs_create_file("parameters",
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_INDEX_TABLE_PARAMS(i),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- for (j = 0; j < TSPP2_NUM_INDEXING_PATTERNS; j++) {
|
||
- snprintf(name, 80, "pattern_%02i", j);
|
||
- debugfs_create_file(name,
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_INDEX_TABLE_PATTEREN(i, j),
|
||
- &fops_iomem_x32);
|
||
-
|
||
- snprintf(name, 80, "mask_%02i", j);
|
||
- debugfs_create_file(name,
|
||
- TSPP2_S_RW,
|
||
- dentry,
|
||
- base + TSPP2_INDEX_TABLE_MASK(i, j),
|
||
- &fops_iomem_x32);
|
||
- }
|
||
- }
|
||
- }
|
||
- dir = debugfs_create_dir("software", device->debugfs_entry);
|
||
- debugfs_create_file("device", S_IRUGO, dir, device,
|
||
- &dbgfs_tspp2_device_fops);
|
||
-
|
||
- dentry = debugfs_create_dir("filters", dir);
|
||
- if (dentry) {
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_FILTERS; i++) {
|
||
- snprintf(name, 20, "filter%03i", i);
|
||
- debugfs_create_file(name, S_IRUGO, dentry,
|
||
- &(device->filters[i]), &dbgfs_filter_fops);
|
||
- }
|
||
- }
|
||
-
|
||
- dentry = debugfs_create_dir("pipes", dir);
|
||
- if (dentry) {
|
||
- for (i = 0; i < TSPP2_NUM_PIPES; i++) {
|
||
- snprintf(name, 20, "pipe%02i", i);
|
||
- debugfs_create_file(name, S_IRUGO, dentry,
|
||
- &(device->pipes[i]), &dbgfs_pipe_fops);
|
||
- }
|
||
- }
|
||
-
|
||
- dentry = debugfs_create_dir("sources", dir);
|
||
- if (dentry) {
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) {
|
||
- snprintf(name, 20, "tsif%d", i);
|
||
- debugfs_create_file(name, S_IRUGO, dentry,
|
||
- &(device->tsif_sources[i]), &dbgfs_src_fops);
|
||
- }
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
|
||
- snprintf(name, 20, "mem%d", i);
|
||
- debugfs_create_file(name, S_IRUGO, dentry,
|
||
- &(device->mem_sources[i]), &dbgfs_src_fops);
|
||
- }
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_debugfs_exit() - TSPP2 device debugfs teardown.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- */
|
||
-static void tspp2_debugfs_exit(struct tspp2_device *device)
|
||
-{
|
||
- debugfs_remove_recursive(device->debugfs_entry);
|
||
- device->debugfs_entry = NULL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_tsif_start() - Start TSIF device HW.
|
||
- *
|
||
- * @tsif_device: TSIF device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_tsif_start(struct tspp2_tsif_device *tsif_device)
|
||
-{
|
||
- u32 ctl;
|
||
-
|
||
- if (tsif_device->ref_count > 0)
|
||
- return 0;
|
||
-
|
||
- ctl = (TSIF_STS_CTL_EN_IRQ | TSIF_STS_CTL_EN_DM |
|
||
- TSIF_STS_CTL_PACK_AVAIL | TSIF_STS_CTL_OVERFLOW |
|
||
- TSIF_STS_CTL_LOST_SYNC | TSIF_STS_CTL_TIMEOUT |
|
||
- TSIF_STS_CTL_PARALLEL);
|
||
-
|
||
- if (tsif_device->clock_inverse)
|
||
- ctl |= TSIF_STS_CTL_INV_CLOCK;
|
||
-
|
||
- if (tsif_device->data_inverse)
|
||
- ctl |= TSIF_STS_CTL_INV_DATA;
|
||
-
|
||
- if (tsif_device->sync_inverse)
|
||
- ctl |= TSIF_STS_CTL_INV_SYNC;
|
||
-
|
||
- if (tsif_device->enable_inverse)
|
||
- ctl |= TSIF_STS_CTL_INV_ENABLE;
|
||
-
|
||
- switch (tsif_device->mode) {
|
||
- case TSPP2_TSIF_MODE_LOOPBACK:
|
||
- ctl |= TSIF_STS_CTL_EN_NULL |
|
||
- TSIF_STS_CTL_EN_ERROR |
|
||
- TSIF_STS_CTL_TEST_MODE;
|
||
- break;
|
||
- case TSPP2_TSIF_MODE_1:
|
||
- ctl |= TSIF_STS_CTL_EN_TIME_LIM | TSIF_STS_CTL_EN_TCR;
|
||
- break;
|
||
- case TSPP2_TSIF_MODE_2:
|
||
- ctl |= TSIF_STS_CTL_EN_TIME_LIM |
|
||
- TSIF_STS_CTL_EN_TCR |
|
||
- TSIF_STS_CTL_MODE_2;
|
||
- break;
|
||
- default:
|
||
- pr_warn("%s: Unknown TSIF mode %d, setting to TSPP2_TSIF_MODE_2\n",
|
||
- __func__, tsif_device->mode);
|
||
- ctl |= TSIF_STS_CTL_EN_TIME_LIM |
|
||
- TSIF_STS_CTL_EN_TCR |
|
||
- TSIF_STS_CTL_MODE_2;
|
||
- break;
|
||
- }
|
||
-
|
||
- writel_relaxed(ctl, tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
- writel_relaxed(tsif_device->time_limit,
|
||
- tsif_device->base + TSPP2_TSIF_TIME_LIMIT);
|
||
- wmb();
|
||
- writel_relaxed(ctl | TSIF_STS_CTL_START,
|
||
- tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
- wmb();
|
||
-
|
||
- ctl = readl_relaxed(tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
- if (ctl & TSIF_STS_CTL_START)
|
||
- tsif_device->ref_count++;
|
||
-
|
||
- return (ctl & TSIF_STS_CTL_START) ? 0 : -EBUSY;
|
||
-}
|
||
-
|
||
-
|
||
-static int tspp2_vbif_clock_start(struct tspp2_device *device)
|
||
-{
|
||
- int ret;
|
||
-
|
||
- if (device->tspp2_vbif_clk) {
|
||
- ret = clk_prepare_enable(device->tspp2_vbif_clk);
|
||
- if (ret) {
|
||
- pr_err("%s: Can't start tspp2_vbif_clk\n", __func__);
|
||
- return ret;
|
||
- }
|
||
- }
|
||
-
|
||
- if (device->vbif_ahb_clk) {
|
||
- ret = clk_prepare_enable(device->vbif_ahb_clk);
|
||
- if (ret) {
|
||
- pr_err("%s: Can't start vbif_ahb_clk\n", __func__);
|
||
- goto disable_vbif_tspp2;
|
||
- }
|
||
- }
|
||
- if (device->vbif_axi_clk) {
|
||
- ret = clk_prepare_enable(device->vbif_axi_clk);
|
||
- if (ret) {
|
||
- pr_err("%s: Can't start vbif_ahb_clk\n", __func__);
|
||
- goto disable_vbif_ahb;
|
||
- }
|
||
- }
|
||
-
|
||
- return 0;
|
||
-
|
||
-disable_vbif_ahb:
|
||
- if (device->vbif_ahb_clk)
|
||
- clk_disable_unprepare(device->vbif_ahb_clk);
|
||
-disable_vbif_tspp2:
|
||
- if (device->tspp2_vbif_clk)
|
||
- clk_disable_unprepare(device->tspp2_vbif_clk);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-static void tspp2_vbif_clock_stop(struct tspp2_device *device)
|
||
-{
|
||
- if (device->tspp2_vbif_clk)
|
||
- clk_disable_unprepare(device->tspp2_vbif_clk);
|
||
-
|
||
- if (device->vbif_ahb_clk)
|
||
- clk_disable_unprepare(device->vbif_ahb_clk);
|
||
-
|
||
- if (device->vbif_axi_clk)
|
||
- clk_disable_unprepare(device->vbif_axi_clk);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_tsif_stop() - Stop TSIF device HW.
|
||
- *
|
||
- * @tsif_device: TSIF device.
|
||
- */
|
||
-static void tspp2_tsif_stop(struct tspp2_tsif_device *tsif_device)
|
||
-{
|
||
- if (tsif_device->ref_count == 0)
|
||
- return;
|
||
-
|
||
- tsif_device->ref_count--;
|
||
-
|
||
- if (tsif_device->ref_count == 0) {
|
||
- writel_relaxed(TSIF_STS_CTL_STOP,
|
||
- tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
- /*
|
||
- * The driver assumes that after this point the TSIF is stopped,
|
||
- * so a memory barrier is required to allow
|
||
- * further register writes.
|
||
- */
|
||
- wmb();
|
||
- }
|
||
-}
|
||
-
|
||
-/* Clock functions */
|
||
-
|
||
-static int tspp2_reg_clock_start(struct tspp2_device *device)
|
||
-{
|
||
- int rc;
|
||
-
|
||
- if (device->tspp2_ahb_clk &&
|
||
- clk_prepare_enable(device->tspp2_ahb_clk) != 0) {
|
||
- pr_err("%s: Can't start tspp2_ahb_clk\n", __func__);
|
||
- return -EBUSY;
|
||
- }
|
||
-
|
||
- if (device->tspp2_core_clk &&
|
||
- clk_prepare_enable(device->tspp2_core_clk) != 0) {
|
||
- pr_err("%s: Can't start tspp2_core_clk\n", __func__);
|
||
- if (device->tspp2_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_ahb_clk);
|
||
- return -EBUSY;
|
||
- }
|
||
-
|
||
- /* Request minimal bandwidth on the bus, required for register access */
|
||
- if (device->bus_client) {
|
||
- rc = msm_bus_scale_client_update_request(device->bus_client, 1);
|
||
- if (rc) {
|
||
- pr_err("%s: Can't enable bus\n", __func__);
|
||
- if (device->tspp2_core_clk)
|
||
- clk_disable_unprepare(device->tspp2_core_clk);
|
||
- if (device->tspp2_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_ahb_clk);
|
||
- return -EBUSY;
|
||
- }
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-static int tspp2_reg_clock_stop(struct tspp2_device *device)
|
||
-{
|
||
- /* Minimize bandwidth bus voting */
|
||
- if (device->bus_client)
|
||
- msm_bus_scale_client_update_request(device->bus_client, 0);
|
||
-
|
||
- if (device->tspp2_core_clk)
|
||
- clk_disable_unprepare(device->tspp2_core_clk);
|
||
-
|
||
- if (device->tspp2_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_ahb_clk);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_clock_start() - Enable the required TSPP2 clocks
|
||
- *
|
||
- * @device: The TSPP2 device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_clock_start(struct tspp2_device *device)
|
||
-{
|
||
- int tspp2_ahb_clk = 0;
|
||
- int tspp2_core_clk = 0;
|
||
- int tspp2_vbif_clk = 0;
|
||
- int tspp2_klm_ahb_clk = 0;
|
||
- int tsif_ref_clk = 0;
|
||
-
|
||
- if (device == NULL) {
|
||
- pr_err("%s: Can't start clocks, invalid device\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (device->tspp2_ahb_clk) {
|
||
- if (clk_prepare_enable(device->tspp2_ahb_clk) != 0) {
|
||
- pr_err("%s: Can't start tspp2_ahb_clk\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
- tspp2_ahb_clk = 1;
|
||
- }
|
||
-
|
||
- if (device->tspp2_core_clk) {
|
||
- if (clk_prepare_enable(device->tspp2_core_clk) != 0) {
|
||
- pr_err("%s: Can't start tspp2_core_clk\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
- tspp2_core_clk = 1;
|
||
- }
|
||
-
|
||
- if (device->tspp2_klm_ahb_clk) {
|
||
- if (clk_prepare_enable(device->tspp2_klm_ahb_clk) != 0) {
|
||
- pr_err("%s: Can't start tspp2_klm_ahb_clk\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
- tspp2_klm_ahb_clk = 1;
|
||
- }
|
||
-
|
||
- if (device->tsif_ref_clk) {
|
||
- if (clk_prepare_enable(device->tsif_ref_clk) != 0) {
|
||
- pr_err("%s: Can't start tsif_ref_clk\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
- tsif_ref_clk = 1;
|
||
- }
|
||
-
|
||
- /* Request Max bandwidth on the bus, required for full operation */
|
||
- if (device->bus_client &&
|
||
- msm_bus_scale_client_update_request(device->bus_client, 2)) {
|
||
- pr_err("%s: Can't enable bus\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_clocks:
|
||
- if (tspp2_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_ahb_clk);
|
||
-
|
||
- if (tspp2_core_clk)
|
||
- clk_disable_unprepare(device->tspp2_core_clk);
|
||
-
|
||
- if (tspp2_vbif_clk)
|
||
- clk_disable_unprepare(device->tspp2_vbif_clk);
|
||
-
|
||
- if (tspp2_klm_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_klm_ahb_clk);
|
||
-
|
||
- if (tsif_ref_clk)
|
||
- clk_disable_unprepare(device->tsif_ref_clk);
|
||
-
|
||
- return -EBUSY;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_clock_stop() - Disable TSPP2 clocks
|
||
- *
|
||
- * @device: The TSPP2 device.
|
||
- */
|
||
-static void tspp2_clock_stop(struct tspp2_device *device)
|
||
-{
|
||
- if (device == NULL) {
|
||
- pr_err("%s: Can't stop clocks, invalid device\n", __func__);
|
||
- return;
|
||
- }
|
||
-
|
||
- /* Minimize bandwidth bus voting */
|
||
- if (device->bus_client)
|
||
- msm_bus_scale_client_update_request(device->bus_client, 0);
|
||
-
|
||
- if (device->tsif_ref_clk)
|
||
- clk_disable_unprepare(device->tsif_ref_clk);
|
||
-
|
||
- if (device->tspp2_klm_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_klm_ahb_clk);
|
||
-
|
||
- if (device->tspp2_core_clk)
|
||
- clk_disable_unprepare(device->tspp2_core_clk);
|
||
-
|
||
- if (device->tspp2_ahb_clk)
|
||
- clk_disable_unprepare(device->tspp2_ahb_clk);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_counters_reset() - Reset a filter's HW counters.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- * @index: Filter context index. Note counters are based on the context
|
||
- * index and not on the filter HW index.
|
||
- */
|
||
-static void tspp2_filter_counters_reset(struct tspp2_device *device, u32 index)
|
||
-{
|
||
- /* Reset filter counters */
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_TSP_SYNC_ERROR(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_ERRED_TSP(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_DISCONTINUITIES(index));
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_FILTER_SCRAMBLING_BITS_DISCARD(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_TSP_TOTAL_NUM(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_DISCONT_INDICATOR(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_TSP_NO_PAYLOAD(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_TSP_DUPLICATE(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_KEY_FETCH_FAILURE(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_DROPPED_PCR(index));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_PES_ERRORS(index));
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_global_hw_reset() - Reset TSPP2 device registers to a default state.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- * @enable_intr: Enable specific interrupts or disable them.
|
||
- *
|
||
- * A helper function called from probe() and remove(), this function resets both
|
||
- * TSIF devices' SW structures and verifies the TSIF HW is stopped. It resets
|
||
- * TSPP2 registers to appropriate default values and makes sure to disable
|
||
- * all sources, filters etc. Finally, it clears all interrupts and unmasks
|
||
- * the "important" interrupts.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_global_hw_reset(struct tspp2_device *device,
|
||
- int enable_intr)
|
||
-{
|
||
- int i, n;
|
||
- unsigned long rate_in_hz = 0;
|
||
- u32 global_irq_en = 0;
|
||
-
|
||
- if (!device) {
|
||
- pr_err("%s: NULL device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- /* Stop TSIF devices */
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) {
|
||
- device->tsif_devices[i].hw_index = i;
|
||
- device->tsif_devices[i].dev = device;
|
||
- device->tsif_devices[i].mode = TSPP2_TSIF_MODE_2;
|
||
- device->tsif_devices[i].clock_inverse = 0;
|
||
- device->tsif_devices[i].data_inverse = 0;
|
||
- device->tsif_devices[i].sync_inverse = 0;
|
||
- device->tsif_devices[i].enable_inverse = 0;
|
||
- device->tsif_devices[i].stat_pkt_write_err = 0;
|
||
- device->tsif_devices[i].stat_pkt_read_err = 0;
|
||
- device->tsif_devices[i].stat_overflow = 0;
|
||
- device->tsif_devices[i].stat_lost_sync = 0;
|
||
- device->tsif_devices[i].stat_timeout = 0;
|
||
- device->tsif_devices[i].time_limit = TSPP2_TSIF_DEF_TIME_LIMIT;
|
||
- /* Set ref_count to 1 to allow stopping HW */
|
||
- device->tsif_devices[i].ref_count = 1;
|
||
- /* This will reset ref_count to 0 */
|
||
- tspp2_tsif_stop(&device->tsif_devices[i]);
|
||
- }
|
||
-
|
||
- /* Reset indexing table registers */
|
||
- for (i = 0; i < TSPP2_NUM_INDEXING_TABLES; i++) {
|
||
- writel_relaxed(0, device->base + TSPP2_INDEX_TABLE_PREFIX(i));
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_INDEX_TABLE_PREFIX_MASK(i));
|
||
- for (n = 0; n < TSPP2_NUM_INDEXING_PATTERNS; n++) {
|
||
- writel_relaxed(0, device->base +
|
||
- TSPP2_INDEX_TABLE_PATTEREN(i, n));
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_INDEX_TABLE_MASK(i, n));
|
||
- }
|
||
- /* Set number of patterns to 0, prefix size to 4 by default */
|
||
- writel_relaxed(0x00000400,
|
||
- device->base + TSPP2_INDEX_TABLE_PARAMS(i));
|
||
- }
|
||
-
|
||
- /* Disable TSIF inputs. Set mode of operation to 16 batches */
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++)
|
||
- writel_relaxed((0x1 << TSIF_INPUT_SRC_CONFIG_16_BATCHES_OFFS),
|
||
- device->base + TSPP2_TSIF_INPUT_SRC_CONFIG(i));
|
||
-
|
||
- /* Reset source related registers and performance counters */
|
||
- for (i = 0; i < TSPP2_NUM_ALL_INPUTS; i++) {
|
||
- writel_relaxed(0, device->base + TSPP2_SRC_DEST_PIPES(i));
|
||
-
|
||
- /* Set source configuration to default values */
|
||
- writel_relaxed(TSPP2_DEFAULT_SRC_CONFIG,
|
||
- device->base + TSPP2_SRC_CONFIG(i));
|
||
- }
|
||
- writel_relaxed(0x000003FF, device->base + TSPP2_SRC_TOTAL_TSP_RESET);
|
||
- writel_relaxed(0x000003FF,
|
||
- device->base + TSPP2_SRC_FILTERED_OUT_TSP_RESET);
|
||
-
|
||
- /* Reset all contexts, each register handles 32 contexts */
|
||
- for (i = 0; i < 4; i++) {
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_TSP_CONTEXT_RESET(i));
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_PES_CONTEXT_RESET(i));
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_INDEXING_CONTEXT_RESET(i));
|
||
- }
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_HW_FILTERS; i++) {
|
||
- /*
|
||
- * Reset operations: put exit operation in all filter operations
|
||
- */
|
||
- for (n = 0; n < TSPP2_MAX_OPS_PER_FILTER; n++) {
|
||
- writel_relaxed(TSPP2_OPCODE_EXIT,
|
||
- device->base + TSPP2_OPCODE(i, n));
|
||
- }
|
||
- /* Disable all HW filters */
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_ENTRY0(i));
|
||
- writel_relaxed(0, device->base + TSPP2_FILTER_ENTRY1(i));
|
||
- }
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_CONTEXTS; i++) {
|
||
- /* Reset filter context-based counters */
|
||
- tspp2_filter_counters_reset(device, i);
|
||
- }
|
||
-
|
||
- /*
|
||
- * Disable memory inputs. Set mode of operation to 16 batches.
|
||
- * Configure last batch to be associated with all memory input sources,
|
||
- * and add a filter to match all PIDs and drop the TS packets in the
|
||
- * last HW filter entry. Use the last context for this filter.
|
||
- */
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++)
|
||
- writel_relaxed(TSPP2_DEFAULT_MEM_SRC_CONFIG,
|
||
- device->base + TSPP2_MEM_INPUT_SRC_CONFIG(i));
|
||
-
|
||
- writel_relaxed(((TSPP2_NUM_CONTEXTS - 1) << FILTER_ENTRY1_CONTEXT_OFFS),
|
||
- device->base + TSPP2_FILTER_ENTRY1((TSPP2_NUM_HW_FILTERS - 1)));
|
||
- writel_relaxed((0x1 << FILTER_ENTRY0_EN_OFFS),
|
||
- device->base + TSPP2_FILTER_ENTRY0((TSPP2_NUM_HW_FILTERS - 1)));
|
||
-
|
||
- /* Reset pipe registers */
|
||
- for (i = 0; i < TSPP2_NUM_PIPES; i++)
|
||
- writel_relaxed(0xFFFF,
|
||
- device->base + TSPP2_PIPE_THRESH_CONFIG(i));
|
||
-
|
||
- writel_relaxed(0, device->base + TSPP2_PIPE_SECURITY);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_DATA_NOT_SENT_ON_PIPE_RESET);
|
||
-
|
||
- /* Set global configuration to default values */
|
||
-
|
||
- /*
|
||
- * Default: minimum time between PCRs = 50msec, STC offset is 0,
|
||
- * transmit PCR on discontinuity.
|
||
- */
|
||
- writel_relaxed(0x00000432, device->base + TSPP2_PCR_GLOBAL_CONFIG);
|
||
-
|
||
- /* Set correct value according to TSPP2 clock: */
|
||
- if (device->tspp2_core_clk) {
|
||
- rate_in_hz = clk_get_rate(device->tspp2_core_clk);
|
||
- writel_relaxed((rate_in_hz / MSEC_PER_SEC),
|
||
- device->base + TSPP2_CLK_TO_PCR_TIME_UNIT);
|
||
- } else {
|
||
- writel_relaxed(0x00000000,
|
||
- device->base + TSPP2_CLK_TO_PCR_TIME_UNIT);
|
||
- }
|
||
-
|
||
- writel_relaxed(0x00000000, device->base + TSPP2_DESC_WAIT_TIMEOUT);
|
||
-
|
||
- /* Clear all global interrupts */
|
||
- writel_relaxed(0xFFFF000F, device->base + TSPP2_GLOBAL_IRQ_CLEAR);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_UNEXPECTED_RST_IRQ_CLEAR);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_WRONG_PIPE_DIR_IRQ_CLEAR);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_QSB_RESPONSE_ERROR_IRQ_CLEAR);
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_KEY_NOT_READY_IRQ_CLEAR);
|
||
-
|
||
- /*
|
||
- * Global interrupts configuration:
|
||
- * Flow Control (per memory source): Disabled
|
||
- * Read Failure (per memory source): Enabled
|
||
- * SC_GO_LOW (aggregate): Enabled
|
||
- * SC_GO_HIGH (aggregate): Enabled
|
||
- * Wrong Pipe Direction (aggregate): Enabled
|
||
- * QSB Response Error (aggregate): Enabled
|
||
- * Unexpected Reset (aggregate): Enabled
|
||
- * Key Not Ready (aggregate): Disabled
|
||
- * Op Encrypt Level Error: Enabled
|
||
- * PES No Sync: Disabled (module parameter)
|
||
- * TSP Invalid Length: Disabled (module parameter)
|
||
- * TSP Invalid AF Control: Disabled (module parameter)
|
||
- */
|
||
- global_irq_en = 0x00FF03E8;
|
||
- if (tspp2_en_invalid_af_ctrl)
|
||
- global_irq_en |=
|
||
- (0x1 << GLOBAL_IRQ_TSP_INVALID_AF_OFFS);
|
||
- if (tspp2_en_invalid_af_length)
|
||
- global_irq_en |= (0x1 << GLOBAL_IRQ_TSP_INVALID_LEN_OFFS);
|
||
- if (tspp2_en_pes_no_sync)
|
||
- global_irq_en |= (0x1 << GLOBAL_IRQ_PES_NO_SYNC_OFFS);
|
||
-
|
||
- if (enable_intr)
|
||
- writel_relaxed(global_irq_en,
|
||
- device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
- else
|
||
- writel_relaxed(0, device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
-
|
||
- if (enable_intr) {
|
||
- /* Enable all pipe related interrupts */
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_UNEXPECTED_RST_IRQ_ENABLE);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_WRONG_PIPE_DIR_IRQ_ENABLE);
|
||
- writel_relaxed(0x7FFFFFFF,
|
||
- device->base + TSPP2_QSB_RESPONSE_ERROR_IRQ_ENABLE);
|
||
- } else {
|
||
- /* Disable all pipe related interrupts */
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_UNEXPECTED_RST_IRQ_ENABLE);
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_WRONG_PIPE_DIR_IRQ_ENABLE);
|
||
- writel_relaxed(0,
|
||
- device->base + TSPP2_QSB_RESPONSE_ERROR_IRQ_ENABLE);
|
||
- }
|
||
-
|
||
- /* Disable Key Ladder interrupts */
|
||
- writel_relaxed(0, device->base + TSPP2_KEY_NOT_READY_IRQ_ENABLE);
|
||
-
|
||
- /*
|
||
- * Clear and disable scrambling control interrupts.
|
||
- * Each register handles 32 filters.
|
||
- */
|
||
- for (i = 0; i < 4; i++) {
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_SC_GO_HIGH_CLEAR(i));
|
||
- writel_relaxed(0, device->base + TSPP2_SC_GO_HIGH_ENABLE(i));
|
||
- writel_relaxed(0xFFFFFFFF,
|
||
- device->base + TSPP2_SC_GO_LOW_CLEAR(i));
|
||
- writel_relaxed(0, device->base + TSPP2_SC_GO_LOW_ENABLE(i));
|
||
- }
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_event_work_handler - Handle the work - invoke the user callback.
|
||
- *
|
||
- * @work: The work information.
|
||
- */
|
||
-static void tspp2_event_work_handler(struct work_struct *work)
|
||
-{
|
||
- struct tspp2_event_work *event_work =
|
||
- container_of(work, struct tspp2_event_work, work);
|
||
- struct tspp2_event_work cb_info = *event_work;
|
||
-
|
||
- if (mutex_lock_interruptible(&event_work->device->mutex))
|
||
- return;
|
||
-
|
||
- list_add_tail(&event_work->link, &event_work->device->free_work_list);
|
||
-
|
||
- mutex_unlock(&event_work->device->mutex);
|
||
-
|
||
- /*
|
||
- * Must run callback with tspp2 device mutex unlocked,
|
||
- * as callback might call tspp2 driver API and cause a deadlock.
|
||
- */
|
||
- if (cb_info.callback)
|
||
- cb_info.callback(cb_info.cookie, cb_info.event_bitmask);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_device_initialize() - Initialize TSPP2 device SW structures.
|
||
- *
|
||
- * @device: TSPP2 device
|
||
- *
|
||
- * Initialize the required SW structures and fields in the TSPP2 device,
|
||
- * including ION client creation, BAM registration, debugfs initialization etc.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_device_initialize(struct tspp2_device *device)
|
||
-{
|
||
- int i, ret;
|
||
-
|
||
- if (!device) {
|
||
- pr_err("%s: NULL device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- /* Register BAM */
|
||
- device->bam_props.summing_threshold = 0x10;
|
||
- device->bam_props.irq = device->bam_irq;
|
||
- device->bam_props.manage = SPS_BAM_MGR_LOCAL;
|
||
-
|
||
- ret = sps_register_bam_device(&device->bam_props, &device->bam_handle);
|
||
- if (ret) {
|
||
- pr_err("%s: failed to register BAM\n", __func__);
|
||
- return ret;
|
||
- }
|
||
- ret = sps_device_reset(device->bam_handle);
|
||
- if (ret) {
|
||
- sps_deregister_bam_device(device->bam_handle);
|
||
- pr_err("%s: error resetting BAM\n", __func__);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- spin_lock_init(&device->spinlock);
|
||
- wakeup_source_init(&device->wakeup_src, dev_name(&device->pdev->dev));
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++)
|
||
- tspp2_tsif_debugfs_init(&device->tsif_devices[i]);
|
||
-
|
||
- /*
|
||
- * The device structure was allocated using devm_kzalloc() so
|
||
- * the memory was initialized to zero. We don't need to specifically set
|
||
- * fields to zero, then. We only set the fields we need to, such as
|
||
- * batch_id.
|
||
- */
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_BATCHES; i++) {
|
||
- device->batches[i].batch_id = i;
|
||
- device->batches[i].src = NULL;
|
||
- INIT_LIST_HEAD(&device->batches[i].link);
|
||
- }
|
||
-
|
||
- /*
|
||
- * We set the device back-pointer in the sources, filters and pipes
|
||
- * databases here, so that back-pointer is always valid (instead of
|
||
- * setting it when opening a source, filter or pipe).
|
||
- */
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++)
|
||
- device->tsif_sources[i].device = device;
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++)
|
||
- device->mem_sources[i].device = device;
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_FILTERS; i++)
|
||
- device->filters[i].device = device;
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_PIPES; i++)
|
||
- device->pipes[i].device = device;
|
||
-
|
||
- /*
|
||
- * Note: tsif_devices are initialized as part of tspp2_global_hw_reset()
|
||
- */
|
||
-
|
||
- device->work_queue =
|
||
- create_singlethread_workqueue(dev_name(device->dev));
|
||
- INIT_LIST_HEAD(&device->free_work_list);
|
||
- for (i = 0; i < TSPP2_NUM_EVENT_WORK_ELEMENTS; i++) {
|
||
- device->work_pool[i].device = device;
|
||
- device->work_pool[i].callback = 0;
|
||
- device->work_pool[i].cookie = 0;
|
||
- device->work_pool[i].event_bitmask = 0;
|
||
- INIT_LIST_HEAD(&device->work_pool[i].link);
|
||
- INIT_WORK(&device->work_pool[i].work,
|
||
- tspp2_event_work_handler);
|
||
-
|
||
- list_add_tail(&device->work_pool[i].link,
|
||
- &device->free_work_list);
|
||
- }
|
||
-
|
||
- device->event_callback = NULL;
|
||
- device->event_cookie = NULL;
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_device_uninitialize() - TSPP2 device teardown and cleanup.
|
||
- *
|
||
- * @device: TSPP2 device
|
||
- *
|
||
- * TSPP2 device teardown: debugfs removal, BAM de-registration etc.
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_device_uninitialize(struct tspp2_device *device)
|
||
-{
|
||
- int i;
|
||
-
|
||
- if (!device) {
|
||
- pr_err("%s: NULL device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- destroy_workqueue(device->work_queue);
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++)
|
||
- tspp2_tsif_debugfs_exit(&device->tsif_devices[i]);
|
||
-
|
||
- /* Need to start clocks for BAM de-registration */
|
||
- if (pm_runtime_get_sync(device->dev) >= 0) {
|
||
- sps_deregister_bam_device(device->bam_handle);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- }
|
||
-
|
||
- wakeup_source_trash(&device->wakeup_src);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_src_disable_internal() - Helper function to disable a source.
|
||
- *
|
||
- * @src: Source to disable.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_src_disable_internal(struct tspp2_src *src)
|
||
-{
|
||
- u32 reg;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!src->enabled) {
|
||
- pr_warn("%s: Source already disabled\n", __func__);
|
||
- return 0;
|
||
- }
|
||
-
|
||
- if ((src->input == TSPP2_INPUT_TSIF0) ||
|
||
- (src->input == TSPP2_INPUT_TSIF1)) {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- reg &= ~(0x1 << TSIF_INPUT_SRC_CONFIG_INPUT_EN_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
-
|
||
- tspp2_tsif_stop(&src->device->tsif_devices[src->input]);
|
||
- } else {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- reg &= ~(0x1 << MEM_INPUT_SRC_CONFIG_INPUT_EN_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- }
|
||
-
|
||
- /*
|
||
- * HW requires we wait for up to 2ms here before closing the pipes
|
||
- * attached to (and used by) this source
|
||
- */
|
||
- udelay(TSPP2_HW_DELAY_USEC);
|
||
-
|
||
- src->enabled = 0;
|
||
- src->device->num_enabled_sources--;
|
||
-
|
||
- if (src->device->num_enabled_sources == 0) {
|
||
- __pm_relax(&src->device->wakeup_src);
|
||
- tspp2_clock_stop(src->device);
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/* TSPP2 device open / close API */
|
||
-
|
||
-/**
|
||
- * tspp2_device_open() - Open a TSPP2 device for use.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_device_open(u32 dev_id)
|
||
-{
|
||
- int rc;
|
||
- u32 reg = 0;
|
||
- struct tspp2_device *device;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (device->opened) {
|
||
- pr_err("%s: Device already opened\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Enable power regulator */
|
||
- rc = regulator_enable(device->gdsc);
|
||
- if (rc)
|
||
- goto err_mutex_unlock;
|
||
-
|
||
- /* Reset TSPP2 core */
|
||
- clk_reset(device->tspp2_core_clk, CLK_RESET_ASSERT);
|
||
- udelay(10);
|
||
- clk_reset(device->tspp2_core_clk, CLK_RESET_DEASSERT);
|
||
-
|
||
- /* Start HW clocks before accessing registers */
|
||
- rc = tspp2_reg_clock_start(device);
|
||
- if (rc)
|
||
- goto err_regulator_disable;
|
||
-
|
||
- rc = tspp2_global_hw_reset(device, 1);
|
||
- if (rc)
|
||
- goto err_stop_clocks;
|
||
-
|
||
- rc = tspp2_device_initialize(device);
|
||
- if (rc)
|
||
- goto err_stop_clocks;
|
||
-
|
||
- reg = readl_relaxed(device->base + TSPP2_VERSION);
|
||
- pr_info("TSPP2 HW Version: Major = %d, Minor = %d, Step = %d\n",
|
||
- ((reg & 0xF0000000) >> VERSION_MAJOR_OFFS),
|
||
- ((reg & 0x0FFF0000) >> VERSION_MINOR_OFFS),
|
||
- ((reg & 0x0000FFFF) >> VERSION_STEP_OFFS));
|
||
-
|
||
- /* Stop HW clocks to save power */
|
||
- tspp2_reg_clock_stop(device);
|
||
-
|
||
- /* Enable runtime power management */
|
||
- pm_runtime_set_autosuspend_delay(device->dev, MSEC_PER_SEC);
|
||
- pm_runtime_use_autosuspend(device->dev);
|
||
- pm_runtime_enable(device->dev);
|
||
-
|
||
- device->opened = 1;
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_stop_clocks:
|
||
- tspp2_reg_clock_stop(device);
|
||
-err_regulator_disable:
|
||
- regulator_disable(device->gdsc);
|
||
-err_mutex_unlock:
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- return rc;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_device_open);
|
||
-
|
||
-/**
|
||
- * tspp2_device_close() - Close a TSPP2 device.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_device_close(u32 dev_id)
|
||
-{
|
||
- int i;
|
||
- int ret = 0;
|
||
- struct tspp2_device *device;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&device->mutex);
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device already closed\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
- device->opened = 0;
|
||
-
|
||
- /*
|
||
- * In case the user has not disabled all the enabled sources, we need
|
||
- * to disable them here, specifically in order to call tspp2_clock_stop,
|
||
- * because the calls to enable and disable the clocks should be
|
||
- * symmetrical (otherwise we cannot put the clocks).
|
||
- */
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) {
|
||
- if (device->tsif_sources[i].enabled)
|
||
- tspp2_src_disable_internal(&device->tsif_sources[i]);
|
||
- }
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
|
||
- if (device->mem_sources[i].enabled)
|
||
- tspp2_src_disable_internal(&device->mem_sources[i]);
|
||
- }
|
||
-
|
||
- /* bring HW registers back to a known state */
|
||
- tspp2_global_hw_reset(device, 0);
|
||
-
|
||
- tspp2_device_uninitialize(device);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- /* Disable runtime power management */
|
||
- pm_runtime_disable(device->dev);
|
||
- pm_runtime_set_suspended(device->dev);
|
||
-
|
||
- if (regulator_disable(device->gdsc))
|
||
- pr_err("%s: Error disabling power regulator\n", __func__);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_device_close);
|
||
-
|
||
-/* Global configuration API */
|
||
-
|
||
-/**
|
||
- * tspp2_config_set() - Set device global configuration.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @cfg: TSPP2 global configuration parameters to set.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_config_set(u32 dev_id, const struct tspp2_config *cfg)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_device *device;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (!cfg) {
|
||
- pr_err("%s: NULL configuration\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (cfg->stc_byte_offset > 3) {
|
||
- pr_err("%s: Invalid stc_byte_offset %d, valid values are 0 - 3\n",
|
||
- __func__, cfg->stc_byte_offset);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (cfg->pcr_on_discontinuity)
|
||
- reg |= (0x1 << PCR_GLOBAL_CONFIG_PCR_ON_DISCONT_OFFS);
|
||
-
|
||
- reg |= (cfg->stc_byte_offset << PCR_GLOBAL_CONFIG_STC_OFFSET_OFFS);
|
||
- reg |= (cfg->min_pcr_interval << PCR_GLOBAL_CONFIG_PCR_INTERVAL_OFFS);
|
||
-
|
||
- writel_relaxed(reg, device->base + TSPP2_PCR_GLOBAL_CONFIG);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_config_set);
|
||
-
|
||
-/**
|
||
- * tspp2_config_get() - Get current global configuration.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @cfg: TSPP2 global configuration parameters.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_config_get(u32 dev_id, struct tspp2_config *cfg)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_device *device;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (!cfg) {
|
||
- pr_err("%s: NULL configuration\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(device->base + TSPP2_PCR_GLOBAL_CONFIG);
|
||
-
|
||
- cfg->pcr_on_discontinuity = ((reg & PCR_GLOBAL_CONFIG_PCR_ON_DISCONT) >>
|
||
- PCR_GLOBAL_CONFIG_PCR_ON_DISCONT_OFFS);
|
||
- cfg->stc_byte_offset = ((reg & PCR_GLOBAL_CONFIG_STC_OFFSET) >>
|
||
- PCR_GLOBAL_CONFIG_STC_OFFSET_OFFS);
|
||
- cfg->min_pcr_interval = ((reg & PCR_GLOBAL_CONFIG_PCR_INTERVAL) >>
|
||
- PCR_GLOBAL_CONFIG_PCR_INTERVAL_OFFS);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_config_get);
|
||
-
|
||
-/* Indexing tables API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_indexing_prefix_set() - Set prefix value and mask of an indexing table.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @table_id: Indexing table ID.
|
||
- * @value: Prefix 4-byte value.
|
||
- * @mask: Prefix 4-byte mask.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_indexing_prefix_set(u32 dev_id,
|
||
- u8 table_id,
|
||
- u32 value,
|
||
- u32 mask)
|
||
-{
|
||
- int ret;
|
||
- u32 reg;
|
||
- u8 size = 0;
|
||
- int i;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_indexing_table *table;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (table_id >= TSPP2_NUM_INDEXING_TABLES) {
|
||
- pr_err("%s: Invalid table ID %d\n", __func__, table_id);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- table = &device->indexing_tables[table_id];
|
||
- table->prefix_value = value;
|
||
- table->prefix_mask = mask;
|
||
-
|
||
- /* HW expects values/masks to be written in Big Endian format */
|
||
- writel_relaxed(cpu_to_be32(value),
|
||
- device->base + TSPP2_INDEX_TABLE_PREFIX(table_id));
|
||
- writel_relaxed(cpu_to_be32(mask),
|
||
- device->base + TSPP2_INDEX_TABLE_PREFIX_MASK(table_id));
|
||
-
|
||
- /* Find the actual size of the prefix and set to HW */
|
||
- reg = readl_relaxed(device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
- for (i = 0; i < 32; i += 8) {
|
||
- if (mask & (0x000000FF << i))
|
||
- size++;
|
||
- }
|
||
- reg &= ~(0x7 << INDEX_TABLE_PARAMS_PREFIX_SIZE_OFFS);
|
||
- reg |= (size << INDEX_TABLE_PARAMS_PREFIX_SIZE_OFFS);
|
||
- writel_relaxed(reg, device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_indexing_prefix_set);
|
||
-
|
||
-/**
|
||
- * tspp2_indexing_patterns_add() - Add patterns to an indexing table.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @table_id: Indexing table ID.
|
||
- * @values: An array of 4-byte pattern values.
|
||
- * @masks: An array of corresponding 4-byte masks.
|
||
- * @patterns_num: Number of patterns in the values / masks arrays.
|
||
- * Up to TSPP2_NUM_INDEXING_PATTERNS.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_indexing_patterns_add(u32 dev_id,
|
||
- u8 table_id,
|
||
- const u32 *values,
|
||
- const u32 *masks,
|
||
- u8 patterns_num)
|
||
-{
|
||
- int ret;
|
||
- int i;
|
||
- u16 offs = 0;
|
||
- u32 reg;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_indexing_table *table;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (table_id >= TSPP2_NUM_INDEXING_TABLES) {
|
||
- pr_err("%s: Invalid table ID %d\n", __func__, table_id);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!values || !masks) {
|
||
- pr_err("%s: NULL values or masks array\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- table = &device->indexing_tables[table_id];
|
||
-
|
||
- if ((table->num_valid_entries + patterns_num) >
|
||
- TSPP2_NUM_INDEXING_PATTERNS) {
|
||
- pr_err("%s: Trying to add too many patterns: current number %d, trying to add %d, maximum allowed %d\n",
|
||
- __func__, table->num_valid_entries, patterns_num,
|
||
- TSPP2_NUM_INDEXING_PATTERNS);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* There's enough room to add all the requested patterns */
|
||
- offs = table->num_valid_entries;
|
||
- for (i = 0; i < patterns_num; i++) {
|
||
- table->entry_value[offs + i] = values[i];
|
||
- table->entry_mask[offs + i] = masks[i];
|
||
- writel_relaxed(cpu_to_be32(values[i]),
|
||
- device->base +
|
||
- TSPP2_INDEX_TABLE_PATTEREN(table_id, offs + i));
|
||
- writel_relaxed(cpu_to_be32(masks[i]), device->base +
|
||
- TSPP2_INDEX_TABLE_MASK(table_id, offs + i));
|
||
- }
|
||
- table->num_valid_entries += patterns_num;
|
||
- reg = readl_relaxed(device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
- reg &= ~(0x1F << INDEX_TABLE_PARAMS_NUM_PATTERNS_OFFS);
|
||
- reg |= (table->num_valid_entries <<
|
||
- INDEX_TABLE_PARAMS_NUM_PATTERNS_OFFS);
|
||
- writel_relaxed(reg, device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_indexing_patterns_add);
|
||
-
|
||
-/**
|
||
- * tspp2_indexing_patterns_clear() - Clear all patterns of an indexing table.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @table_id: Indexing table ID.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_indexing_patterns_clear(u32 dev_id,
|
||
- u8 table_id)
|
||
-{
|
||
- int ret;
|
||
- int i;
|
||
- u32 reg;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_indexing_table *table;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (table_id >= TSPP2_NUM_INDEXING_TABLES) {
|
||
- pr_err("%s: Invalid table ID %d\n", __func__, table_id);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- table = &device->indexing_tables[table_id];
|
||
-
|
||
- for (i = 0; i < table->num_valid_entries; i++) {
|
||
- table->entry_value[i] = 0;
|
||
- table->entry_mask[i] = 0;
|
||
- writel_relaxed(0, device->base +
|
||
- TSPP2_INDEX_TABLE_PATTEREN(table_id, i));
|
||
- writel_relaxed(0, device->base +
|
||
- TSPP2_INDEX_TABLE_MASK(table_id, i));
|
||
-
|
||
- }
|
||
- table->num_valid_entries = 0;
|
||
- reg = readl_relaxed(device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
- reg &= ~(0x1F << INDEX_TABLE_PARAMS_NUM_PATTERNS_OFFS);
|
||
- writel_relaxed(reg, device->base + TSPP2_INDEX_TABLE_PARAMS(table_id));
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_indexing_patterns_clear);
|
||
-
|
||
-/* Pipe API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_memory_init() - Initialize pipe memory helper function.
|
||
- *
|
||
- * @pipe: The pipe to work on.
|
||
- *
|
||
- * The user is responsible for allocating the pipe's memory buffer via ION.
|
||
- * This helper function maps the given buffer to TSPP2 IOMMU memory space,
|
||
- * and sets the pipe's secure bit.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe)
|
||
-{
|
||
- int ret = 0;
|
||
- u32 reg;
|
||
- size_t align;
|
||
- unsigned long dummy_size = 0;
|
||
- size_t len = 0;
|
||
- int domain = 0;
|
||
- int partition = 0;
|
||
- int hlos_group_attached = 0;
|
||
- int cpz_group_attached = 0;
|
||
- int vbif_clk_started = 0;
|
||
-
|
||
- if (pipe->cfg.is_secure) {
|
||
- domain = pipe->device->iommu_info.cpz_domain_num;
|
||
- partition = pipe->device->iommu_info.cpz_partition;
|
||
- align = SZ_1M;
|
||
- } else {
|
||
- domain = pipe->device->iommu_info.hlos_domain_num;
|
||
- partition = pipe->device->iommu_info.hlos_partition;
|
||
- align = SZ_4K;
|
||
- }
|
||
-
|
||
- if (tspp2_iommu_bypass) {
|
||
- ret = ion_phys(pipe->cfg.ion_client,
|
||
- pipe->cfg.buffer_handle, &pipe->iova, &len);
|
||
-
|
||
- dummy_size = 0;
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: Failed to get buffer physical address, ret = %d\n",
|
||
- __func__, ret);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- if ((pipe->device->num_secured_opened_pipes +
|
||
- pipe->device->num_non_secured_opened_pipes) == 0) {
|
||
- ret = tspp2_vbif_clock_start(pipe->device);
|
||
- if (ret) {
|
||
- pr_err(
|
||
- "%s: tspp2_vbif_clock_start failed, ret=%d\n",
|
||
- __func__, ret);
|
||
- return ret;
|
||
- }
|
||
- vbif_clk_started = 1;
|
||
- }
|
||
- } else {
|
||
- /*
|
||
- * We need to attach the group to enable the IOMMU and support
|
||
- * the required memory mapping. This needs to be done before
|
||
- * the first mapping is performed, so the number of opened pipes
|
||
- * (of each type: secure or non-secure) is used as a
|
||
- * reference count. Note that since the pipe descriptors are
|
||
- * always allocated from HLOS domain, the HLOS group must be
|
||
- * attached regardless of the pipe's security configuration.
|
||
- * The mutex is taken at this point so there is no problem with
|
||
- * synchronization.
|
||
- */
|
||
- if ((pipe->device->num_secured_opened_pipes +
|
||
- pipe->device->num_non_secured_opened_pipes) == 0) {
|
||
- ret = tspp2_vbif_clock_start(pipe->device);
|
||
- if (ret) {
|
||
- pr_err("%s: tspp2_vbif_clock_start failed, ret=%d\n",
|
||
- __func__, ret);
|
||
- goto err_out;
|
||
- }
|
||
- vbif_clk_started = 1;
|
||
-
|
||
- pr_debug("%s: attaching HLOS group\n", __func__);
|
||
- ret = iommu_attach_group(
|
||
- pipe->device->iommu_info.hlos_domain,
|
||
- pipe->device->iommu_info.hlos_group);
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: Failed attaching IOMMU HLOS group, %d\n",
|
||
- __func__, ret);
|
||
- goto err_out;
|
||
- }
|
||
- hlos_group_attached = 1;
|
||
- }
|
||
-
|
||
- if (pipe->cfg.is_secure &&
|
||
- (pipe->device->num_secured_opened_pipes == 0)) {
|
||
- pr_debug("%s: attaching CPZ group\n", __func__);
|
||
- ret = iommu_attach_group(
|
||
- pipe->device->iommu_info.cpz_domain,
|
||
- pipe->device->iommu_info.cpz_group);
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: Failed attaching IOMMU CPZ group, %d\n",
|
||
- __func__, ret);
|
||
- goto err_out;
|
||
- }
|
||
- cpz_group_attached = 1;
|
||
- }
|
||
-
|
||
- /* Map to TSPP2 IOMMU */
|
||
- ret = ion_map_iommu(pipe->cfg.ion_client,
|
||
- pipe->cfg.buffer_handle,
|
||
- domain,
|
||
- partition,
|
||
- align, 0, &pipe->iova,
|
||
- &dummy_size, 0, 0); /* Uncached mapping */
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: Failed mapping buffer to TSPP2, %d\n",
|
||
- __func__, ret);
|
||
- goto err_out;
|
||
- }
|
||
- }
|
||
-
|
||
- if (pipe->cfg.is_secure) {
|
||
- reg = readl_relaxed(pipe->device->base + TSPP2_PIPE_SECURITY);
|
||
- reg |= (0x1 << pipe->hw_index);
|
||
- writel_relaxed(reg, pipe->device->base + TSPP2_PIPE_SECURITY);
|
||
- }
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_out:
|
||
- if (hlos_group_attached) {
|
||
- iommu_detach_group(pipe->device->iommu_info.hlos_domain,
|
||
- pipe->device->iommu_info.hlos_group);
|
||
- }
|
||
-
|
||
- if (cpz_group_attached) {
|
||
- iommu_detach_group(pipe->device->iommu_info.cpz_domain,
|
||
- pipe->device->iommu_info.cpz_group);
|
||
- }
|
||
-
|
||
- if (vbif_clk_started)
|
||
- tspp2_vbif_clock_stop(pipe->device);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_memory_terminate() - Unmap pipe memory.
|
||
- *
|
||
- * @pipe: The pipe to work on.
|
||
- *
|
||
- * Unmap the pipe's memory and clear the pipe's secure bit.
|
||
- */
|
||
-static void tspp2_pipe_memory_terminate(struct tspp2_pipe *pipe)
|
||
-{
|
||
- u32 reg;
|
||
- int domain = 0;
|
||
- int partition = 0;
|
||
-
|
||
- if (pipe->cfg.is_secure) {
|
||
- domain = pipe->device->iommu_info.cpz_domain_num;
|
||
- partition = pipe->device->iommu_info.cpz_partition;
|
||
- } else {
|
||
- domain = pipe->device->iommu_info.hlos_domain_num;
|
||
- partition = pipe->device->iommu_info.hlos_partition;
|
||
- }
|
||
-
|
||
- if (!tspp2_iommu_bypass) {
|
||
- ion_unmap_iommu(pipe->cfg.ion_client,
|
||
- pipe->cfg.buffer_handle,
|
||
- domain,
|
||
- partition);
|
||
-
|
||
- /*
|
||
- * Opposite to what is done in tspp2_pipe_memory_init(),
|
||
- * here we detach the IOMMU group when it is no longer in use.
|
||
- */
|
||
- if (pipe->cfg.is_secure &&
|
||
- (pipe->device->num_secured_opened_pipes == 0)) {
|
||
- pr_debug("%s: detaching CPZ group\n", __func__);
|
||
- iommu_detach_group(
|
||
- pipe->device->iommu_info.cpz_domain,
|
||
- pipe->device->iommu_info.cpz_group);
|
||
- }
|
||
-
|
||
- if ((pipe->device->num_secured_opened_pipes +
|
||
- pipe->device->num_non_secured_opened_pipes) == 0) {
|
||
- pr_debug("%s: detaching HLOS group\n", __func__);
|
||
- iommu_detach_group(
|
||
- pipe->device->iommu_info.hlos_domain,
|
||
- pipe->device->iommu_info.hlos_group);
|
||
- tspp2_vbif_clock_stop(pipe->device);
|
||
- }
|
||
- } else if ((pipe->device->num_secured_opened_pipes +
|
||
- pipe->device->num_non_secured_opened_pipes) == 0) {
|
||
- tspp2_vbif_clock_stop(pipe->device);
|
||
- }
|
||
-
|
||
- pipe->iova = 0;
|
||
-
|
||
- if (pipe->cfg.is_secure) {
|
||
- reg = readl_relaxed(pipe->device->base + TSPP2_PIPE_SECURITY);
|
||
- reg &= ~(0x1 << pipe->hw_index);
|
||
- writel_relaxed(reg, pipe->device->base + TSPP2_PIPE_SECURITY);
|
||
- }
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_sps_pipe_init() - BAM SPS pipe configuration and initialization
|
||
- *
|
||
- * @pipe: The pipe to work on.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_sps_pipe_init(struct tspp2_pipe *pipe)
|
||
-{
|
||
- u32 descriptors_num;
|
||
- unsigned long dummy_size = 0;
|
||
- int ret = 0;
|
||
- int iommu_mapped = 0;
|
||
-
|
||
- if (pipe->cfg.buffer_size % pipe->cfg.sps_cfg.descriptor_size) {
|
||
- pr_err(
|
||
- "%s: Buffer size %d is not aligned to descriptor size %d\n",
|
||
- __func__, pipe->cfg.buffer_size,
|
||
- pipe->cfg.sps_cfg.descriptor_size);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- pipe->sps_pipe = sps_alloc_endpoint();
|
||
- if (!pipe->sps_pipe) {
|
||
- pr_err("%s: Failed to allocate BAM pipe\n", __func__);
|
||
- return -ENOMEM;
|
||
- }
|
||
-
|
||
- /* get default configuration */
|
||
- sps_get_config(pipe->sps_pipe, &pipe->sps_connect_cfg);
|
||
- if (pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_INPUT) {
|
||
- pipe->sps_connect_cfg.mode = SPS_MODE_DEST;
|
||
- pipe->sps_connect_cfg.source = SPS_DEV_HANDLE_MEM;
|
||
- pipe->sps_connect_cfg.destination = pipe->device->bam_handle;
|
||
- pipe->sps_connect_cfg.dest_pipe_index = pipe->hw_index;
|
||
- } else {
|
||
- pipe->sps_connect_cfg.mode = SPS_MODE_SRC;
|
||
- pipe->sps_connect_cfg.source = pipe->device->bam_handle;
|
||
- pipe->sps_connect_cfg.destination = SPS_DEV_HANDLE_MEM;
|
||
- pipe->sps_connect_cfg.src_pipe_index = pipe->hw_index;
|
||
- }
|
||
- pipe->sps_connect_cfg.desc.base = NULL;
|
||
- pipe->sps_connect_cfg.options = pipe->cfg.sps_cfg.setting;
|
||
- descriptors_num = (pipe->cfg.buffer_size /
|
||
- pipe->cfg.sps_cfg.descriptor_size);
|
||
-
|
||
- /*
|
||
- * If size of descriptors FIFO can hold N descriptors, we can submit
|
||
- * (N-1) descriptors only, therefore we allocate extra descriptor
|
||
- */
|
||
- descriptors_num++;
|
||
- pipe->sps_connect_cfg.desc.size = (descriptors_num *
|
||
- sizeof(struct sps_iovec));
|
||
-
|
||
- if (tspp2_iommu_bypass) {
|
||
- pipe->sps_connect_cfg.desc.base = dma_alloc_coherent(NULL,
|
||
- pipe->sps_connect_cfg.desc.size,
|
||
- &pipe->sps_connect_cfg.desc.phys_base,
|
||
- GFP_KERNEL);
|
||
-
|
||
- if (!pipe->sps_connect_cfg.desc.base) {
|
||
- pr_err("%s: Failed to allocate descriptor FIFO\n",
|
||
- __func__);
|
||
- ret = -ENOMEM;
|
||
- goto init_sps_failed_free_endpoint;
|
||
- }
|
||
- } else {
|
||
- pipe->desc_ion_handle = ion_alloc(pipe->cfg.ion_client,
|
||
- pipe->sps_connect_cfg.desc.size,
|
||
- SZ_4K, ION_HEAP(ION_IOMMU_HEAP_ID), 0);
|
||
-
|
||
- if (!pipe->desc_ion_handle) {
|
||
- pr_err("%s: Failed to allocate descriptors via ION\n",
|
||
- __func__);
|
||
- ret = -ENOMEM;
|
||
- goto init_sps_failed_free_endpoint;
|
||
- }
|
||
-
|
||
- ret = ion_map_iommu(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle,
|
||
- pipe->device->iommu_info.hlos_domain_num,
|
||
- pipe->device->iommu_info.hlos_partition,
|
||
- SZ_4K, 0,
|
||
- &pipe->sps_connect_cfg.desc.phys_base,
|
||
- &dummy_size, 0, 0); /* Uncached mapping */
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: Failed mapping descriptors to IOMMU\n",
|
||
- __func__);
|
||
- goto init_sps_failed_free_mem;
|
||
- }
|
||
-
|
||
- iommu_mapped = 1;
|
||
-
|
||
- pipe->sps_connect_cfg.desc.base =
|
||
- ion_map_kernel(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle);
|
||
-
|
||
- if (!pipe->sps_connect_cfg.desc.base) {
|
||
- pr_err("%s: Failed mapping descriptors to kernel\n",
|
||
- __func__);
|
||
- ret = -ENOMEM;
|
||
- goto init_sps_failed_free_mem;
|
||
- }
|
||
- }
|
||
-
|
||
- ret = sps_connect(pipe->sps_pipe, &pipe->sps_connect_cfg);
|
||
- if (ret) {
|
||
- pr_err("%s: Failed to connect BAM, %d\n", __func__, ret);
|
||
- goto init_sps_failed_free_mem;
|
||
- }
|
||
-
|
||
- pipe->sps_event.options = pipe->cfg.sps_cfg.wakeup_events;
|
||
- if (pipe->sps_event.options) {
|
||
- pipe->sps_event.mode = SPS_TRIGGER_CALLBACK;
|
||
- pipe->sps_event.callback = pipe->cfg.sps_cfg.callback;
|
||
- pipe->sps_event.xfer_done = NULL;
|
||
- pipe->sps_event.user = pipe->cfg.sps_cfg.user_info;
|
||
-
|
||
- ret = sps_register_event(pipe->sps_pipe, &pipe->sps_event);
|
||
- if (ret) {
|
||
- pr_err("%s: Failed to register pipe event, %d\n",
|
||
- __func__, ret);
|
||
- goto init_sps_failed_free_connection;
|
||
- }
|
||
- }
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-init_sps_failed_free_connection:
|
||
- sps_disconnect(pipe->sps_pipe);
|
||
-init_sps_failed_free_mem:
|
||
- if (tspp2_iommu_bypass) {
|
||
- dma_free_coherent(NULL, pipe->sps_connect_cfg.desc.size,
|
||
- pipe->sps_connect_cfg.desc.base,
|
||
- pipe->sps_connect_cfg.desc.phys_base);
|
||
- } else {
|
||
- if (pipe->sps_connect_cfg.desc.base)
|
||
- ion_unmap_kernel(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle);
|
||
-
|
||
- if (iommu_mapped) {
|
||
- ion_unmap_iommu(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle,
|
||
- pipe->device->iommu_info.hlos_domain_num,
|
||
- pipe->device->iommu_info.hlos_partition);
|
||
- }
|
||
-
|
||
- ion_free(pipe->cfg.ion_client, pipe->desc_ion_handle);
|
||
- }
|
||
-init_sps_failed_free_endpoint:
|
||
- sps_free_endpoint(pipe->sps_pipe);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_sps_queue_descriptors() - Queue BAM SPS descriptors
|
||
- *
|
||
- * @pipe: The pipe to work on.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_sps_queue_descriptors(struct tspp2_pipe *pipe)
|
||
-{
|
||
- int ret = 0;
|
||
- u32 data_offset = 0;
|
||
- u32 desc_length = pipe->cfg.sps_cfg.descriptor_size;
|
||
- u32 desc_flags = pipe->cfg.sps_cfg.descriptor_flags;
|
||
- u32 data_length = pipe->cfg.buffer_size;
|
||
-
|
||
- while (data_length > 0) {
|
||
- ret = sps_transfer_one(pipe->sps_pipe,
|
||
- pipe->iova + data_offset,
|
||
- desc_length,
|
||
- pipe->cfg.sps_cfg.user_info,
|
||
- desc_flags);
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: sps_transfer_one failed, %d\n",
|
||
- __func__, ret);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- data_offset += desc_length;
|
||
- data_length -= desc_length;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_sps_pipe_terminate() - Disconnect and terminate SPS BAM pipe
|
||
- *
|
||
- * @pipe: The pipe to work on.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_sps_pipe_terminate(struct tspp2_pipe *pipe)
|
||
-{
|
||
- int ret;
|
||
-
|
||
- ret = sps_disconnect(pipe->sps_pipe);
|
||
- if (ret) {
|
||
- pr_err("%s: failed to disconnect BAM pipe, %d\n",
|
||
- __func__, ret);
|
||
- return ret;
|
||
- }
|
||
- if (tspp2_iommu_bypass) {
|
||
- dma_free_coherent(NULL, pipe->sps_connect_cfg.desc.size,
|
||
- pipe->sps_connect_cfg.desc.base,
|
||
- pipe->sps_connect_cfg.desc.phys_base);
|
||
- } else {
|
||
- ion_unmap_kernel(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle);
|
||
-
|
||
- ion_unmap_iommu(pipe->cfg.ion_client,
|
||
- pipe->desc_ion_handle,
|
||
- pipe->device->iommu_info.hlos_domain_num,
|
||
- pipe->device->iommu_info.hlos_partition);
|
||
-
|
||
- ion_free(pipe->cfg.ion_client, pipe->desc_ion_handle);
|
||
- }
|
||
- pipe->sps_connect_cfg.desc.base = NULL;
|
||
-
|
||
- ret = sps_free_endpoint(pipe->sps_pipe);
|
||
- if (ret) {
|
||
- pr_err("%s: failed to release BAM end-point, %d\n",
|
||
- __func__, ret);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_open() - Open a pipe for use.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @cfg: Pipe configuration parameters.
|
||
- * @iova: TSPP2 IOMMU virtual address of the pipe's buffer.
|
||
- * @pipe_handle: Opened pipe handle.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_pipe_open(u32 dev_id,
|
||
- const struct tspp2_pipe_config_params *cfg,
|
||
- ion_phys_addr_t *iova,
|
||
- u32 *pipe_handle)
|
||
-{
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_pipe *pipe;
|
||
- int i;
|
||
- int ret = 0;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- if (!cfg || !iova || !pipe_handle) {
|
||
- pr_err("%s: Invalid parameters\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Some minimal sanity tests on the pipe configuration: */
|
||
- if (!cfg->ion_client || !cfg->buffer_handle) {
|
||
- pr_err("%s: Invalid parameters\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Find a free pipe */
|
||
- for (i = 0; i < TSPP2_NUM_PIPES; i++) {
|
||
- pipe = &device->pipes[i];
|
||
- if (!pipe->opened)
|
||
- break;
|
||
- }
|
||
- if (i == TSPP2_NUM_PIPES) {
|
||
- pr_err("%s: No available pipes\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
-
|
||
- pipe->hw_index = i;
|
||
- /* Actual pipe threshold is set when the pipe is attached to a source */
|
||
- pipe->threshold = 0;
|
||
- pipe->cfg = *cfg;
|
||
- pipe->ref_cnt = 0;
|
||
- /* device back-pointer is already initialized, always remains valid */
|
||
-
|
||
- ret = tspp2_pipe_memory_init(pipe);
|
||
- if (ret) {
|
||
- pr_err("%s: Error initializing pipe memory\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return ret;
|
||
- }
|
||
- ret = tspp2_sps_pipe_init(pipe);
|
||
- if (ret) {
|
||
- pr_err("%s: Error initializing BAM pipe\n", __func__);
|
||
- tspp2_pipe_memory_terminate(pipe);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- /* For output pipes, we queue BAM descriptors here so they are ready */
|
||
- if (pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_OUTPUT) {
|
||
- ret = tspp2_sps_queue_descriptors(pipe);
|
||
- if (ret) {
|
||
- pr_err("%s: Error queuing BAM pipe descriptors\n",
|
||
- __func__);
|
||
- tspp2_sps_pipe_terminate(pipe);
|
||
- tspp2_pipe_memory_terminate(pipe);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return ret;
|
||
- }
|
||
- }
|
||
-
|
||
- /* Reset counter */
|
||
- writel_relaxed((0x1 << pipe->hw_index),
|
||
- device->base + TSPP2_DATA_NOT_SENT_ON_PIPE_RESET);
|
||
-
|
||
- /* Return handle to the caller */
|
||
- *pipe_handle = (u32)pipe;
|
||
- *iova = pipe->iova;
|
||
-
|
||
- pipe->opened = 1;
|
||
- if (pipe->cfg.is_secure)
|
||
- device->num_secured_opened_pipes++;
|
||
- else
|
||
- device->num_non_secured_opened_pipes++;
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_pipe_open);
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_close() - Close an opened pipe.
|
||
- *
|
||
- * @pipe_handle: Pipe to be closed.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_pipe_close(u32 pipe_handle)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
-
|
||
- if (!pipe) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(pipe->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&pipe->device->mutex);
|
||
-
|
||
- if (!pipe->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe already closed\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (pipe->ref_cnt > 0) {
|
||
- pr_err("%s: Pipe %u is still attached to a source\n",
|
||
- __func__, pipe_handle);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Note: need to decrement the pipe reference count here, before
|
||
- * calling tspp2_pipe_memory_terminate().
|
||
- */
|
||
- if (pipe->cfg.is_secure)
|
||
- pipe->device->num_secured_opened_pipes--;
|
||
- else
|
||
- pipe->device->num_non_secured_opened_pipes--;
|
||
-
|
||
- tspp2_sps_pipe_terminate(pipe);
|
||
- tspp2_pipe_memory_terminate(pipe);
|
||
-
|
||
- pipe->iova = 0;
|
||
- pipe->opened = 0;
|
||
-
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_pipe_close);
|
||
-
|
||
-/* Source API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_src_open() - Open a new source for use.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @cfg: Source configuration parameters.
|
||
- * @src_handle: Opened source handle.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_open(u32 dev_id,
|
||
- struct tspp2_src_cfg *cfg,
|
||
- u32 *src_handle)
|
||
-{
|
||
- int ret;
|
||
- int i;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_src *src;
|
||
- enum tspp2_src_input input;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
- if (!src_handle) {
|
||
- pr_err("%s: Invalid source handle pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!cfg) {
|
||
- pr_err("%s: Invalid configuration parameters\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex)) {
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- input = cfg->input;
|
||
- if ((input == TSPP2_INPUT_TSIF0) || (input == TSPP2_INPUT_TSIF1)) {
|
||
- /* Input from TSIF */
|
||
- if (device->tsif_sources[input].opened) {
|
||
- pr_err("%s: TSIF input %d already opened\n",
|
||
- __func__, input);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
- src = &device->tsif_sources[input];
|
||
-
|
||
- /*
|
||
- * When writing to HW registers that are relevant to sources
|
||
- * of both TSIF and memory input types, the register offsets
|
||
- * for the TSIF-related registers come after the memory-related
|
||
- * registers. For example: for TSPP2_SRC_CONFIG(n), n=[0..9],
|
||
- * indexes 0..7 are for memory inputs, and indexes 8, 9 are
|
||
- * for TSIF inputs.
|
||
- */
|
||
- src->hw_index = TSPP2_NUM_MEM_INPUTS + input;
|
||
-
|
||
- /* Save TSIF source parameters in TSIF device */
|
||
- device->tsif_devices[input].mode =
|
||
- cfg->params.tsif_params.tsif_mode;
|
||
- device->tsif_devices[input].clock_inverse =
|
||
- cfg->params.tsif_params.clock_inverse;
|
||
- device->tsif_devices[input].data_inverse =
|
||
- cfg->params.tsif_params.data_inverse;
|
||
- device->tsif_devices[input].sync_inverse =
|
||
- cfg->params.tsif_params.sync_inverse;
|
||
- device->tsif_devices[input].enable_inverse =
|
||
- cfg->params.tsif_params.enable_inverse;
|
||
- } else {
|
||
- /* Input from memory */
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
|
||
- if (!device->mem_sources[i].opened)
|
||
- break;
|
||
- }
|
||
- if (i == TSPP2_NUM_MEM_INPUTS) {
|
||
- pr_err("%s: No memory inputs available\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
-
|
||
- src = &device->mem_sources[i];
|
||
- src->hw_index = i;
|
||
- }
|
||
-
|
||
- src->opened = 1;
|
||
- src->input = input;
|
||
- src->pkt_format = TSPP2_PACKET_FORMAT_188_RAW; /* default value */
|
||
- src->scrambling_bits_monitoring = TSPP2_SRC_SCRAMBLING_MONITOR_NONE;
|
||
- INIT_LIST_HEAD(&src->batches_list);
|
||
- INIT_LIST_HEAD(&src->filters_list);
|
||
- src->input_pipe = NULL;
|
||
- INIT_LIST_HEAD(&src->output_pipe_list);
|
||
- src->num_associated_batches = 0;
|
||
- src->num_associated_pipes = 0;
|
||
- src->num_associated_filters = 0;
|
||
- src->reserved_filter_hw_index = 0;
|
||
- src->event_callback = NULL;
|
||
- src->event_cookie = NULL;
|
||
- src->event_bitmask = 0;
|
||
- src->enabled = 0;
|
||
- /* device back-pointer is already initialized, always remains valid */
|
||
-
|
||
- /* Reset source-related registers */
|
||
- if ((input == TSPP2_INPUT_TSIF0) || (input == TSPP2_INPUT_TSIF1)) {
|
||
- writel_relaxed((0x1 << TSIF_INPUT_SRC_CONFIG_16_BATCHES_OFFS),
|
||
- device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- } else {
|
||
- /*
|
||
- * Disable memory inputs. Set mode of operation to 16 batches.
|
||
- * Configure last batch to be associated with this source.
|
||
- */
|
||
- writel_relaxed(TSPP2_DEFAULT_MEM_SRC_CONFIG,
|
||
- device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- }
|
||
- writel_relaxed(0, device->base +
|
||
- TSPP2_SRC_DEST_PIPES(src->hw_index));
|
||
- writel_relaxed(TSPP2_DEFAULT_SRC_CONFIG, device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
- writel_relaxed((0x1 << src->hw_index),
|
||
- device->base + TSPP2_SRC_TOTAL_TSP_RESET);
|
||
- writel_relaxed((0x1 << src->hw_index),
|
||
- device->base + TSPP2_SRC_FILTERED_OUT_TSP_RESET);
|
||
-
|
||
- /* Return handle to the caller */
|
||
- *src_handle = (u32)src;
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_open);
|
||
-
|
||
-/**
|
||
- * tspp2_src_close() - Close an opened source.
|
||
- *
|
||
- * @src_handle: Source to be closed.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_close(u32 src_handle)
|
||
-{
|
||
- unsigned long flags;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- mutex_lock(&src->device->mutex);
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source already closed\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (src->enabled) {
|
||
- pr_err("%s: Source needs to be disabled before it can be closed\n",
|
||
- __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /* Verify resources have been released by the caller */
|
||
- if ((src->num_associated_batches > 0) ||
|
||
- (src->num_associated_pipes > 0) ||
|
||
- (src->num_associated_filters > 0)) {
|
||
- pr_err("%s: Source's resources need to be removed before it can be closed\n",
|
||
- __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Most fields are reset to default values when opening a source, so
|
||
- * there is no need to reset them all here. We only need to mark the
|
||
- * source as closed.
|
||
- */
|
||
- src->opened = 0;
|
||
- spin_lock_irqsave(&src->device->spinlock, flags);
|
||
- src->event_callback = NULL;
|
||
- src->event_cookie = NULL;
|
||
- src->event_bitmask = 0;
|
||
- spin_unlock_irqrestore(&src->device->spinlock, flags);
|
||
- src->enabled = 0;
|
||
-
|
||
- /*
|
||
- * Source-related HW registers are reset when opening a source, so
|
||
- * we don't reser them here. Note that a source is disabled before
|
||
- * it is closed, so no need to disable it here either.
|
||
- */
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_close);
|
||
-
|
||
-/**
|
||
- * tspp2_src_parsing_option_set() - Set source parsing configuration option.
|
||
- *
|
||
- * @src_handle: Source to configure.
|
||
- * @option: Parsing configuration option to enable / disable.
|
||
- * @enable: Enable / disable option.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_parsing_option_set(u32 src_handle,
|
||
- enum tspp2_src_parsing_option option,
|
||
- int enable)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- switch (option) {
|
||
- case TSPP2_SRC_PARSING_OPT_CHECK_CONTINUITY:
|
||
- if (enable)
|
||
- reg |= (0x1 << SRC_CONFIG_CHECK_CONT_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_CHECK_CONT_OFFS);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_IGNORE_DISCONTINUITY:
|
||
- if (enable)
|
||
- reg |= (0x1 << SRC_CONFIG_IGNORE_DISCONT_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_IGNORE_DISCONT_OFFS);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_ASSUME_DUPLICATE_PACKETS:
|
||
- if (enable)
|
||
- reg |= (0x1 << SRC_CONFIG_ASSUME_DUPLICATES_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_ASSUME_DUPLICATES_OFFS);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_DISCARD_INVALID_AF_PACKETS:
|
||
- if (enable)
|
||
- reg |= (0x1 << SRC_CONFIG_DISCARD_INVALID_AF_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_DISCARD_INVALID_AF_OFFS);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_VERIFY_PES_START:
|
||
- if (enable)
|
||
- reg |= (0x1 << SRC_CONFIG_VERIFY_PES_START_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_VERIFY_PES_START_OFFS);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Invalid option %d\n", __func__, option);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_parsing_option_set);
|
||
-
|
||
-/**
|
||
- * tspp2_src_parsing_option_get() - Get source parsing configuration option.
|
||
- *
|
||
- * @src_handle: Source handle.
|
||
- * @option: Parsing configuration option to get.
|
||
- * @enable: Option's enable / disable indication.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_parsing_option_get(u32 src_handle,
|
||
- enum tspp2_src_parsing_option option,
|
||
- int *enable)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!enable) {
|
||
- pr_err("%s: NULL pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- switch (option) {
|
||
- case TSPP2_SRC_PARSING_OPT_CHECK_CONTINUITY:
|
||
- *enable = ((reg >> SRC_CONFIG_CHECK_CONT_OFFS) & 0x1);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_IGNORE_DISCONTINUITY:
|
||
- *enable = ((reg >> SRC_CONFIG_IGNORE_DISCONT_OFFS) & 0x1);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_ASSUME_DUPLICATE_PACKETS:
|
||
- *enable = ((reg >> SRC_CONFIG_ASSUME_DUPLICATES_OFFS) & 0x1);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_DISCARD_INVALID_AF_PACKETS:
|
||
- *enable = ((reg >> SRC_CONFIG_DISCARD_INVALID_AF_OFFS) & 0x1);
|
||
- break;
|
||
- case TSPP2_SRC_PARSING_OPT_VERIFY_PES_START:
|
||
- *enable = ((reg >> SRC_CONFIG_VERIFY_PES_START_OFFS) & 0x1);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Invalid option %d\n", __func__, option);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_parsing_option_get);
|
||
-
|
||
-/**
|
||
- * tspp2_src_sync_byte_config_set() - Set source sync byte configuration.
|
||
- *
|
||
- * @src_handle: Source to configure.
|
||
- * @check_sync_byte: Check TS packet sync byte.
|
||
- * @sync_byte_value: Sync byte value to check (e.g., 0x47).
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_sync_byte_config_set(u32 src_handle,
|
||
- int check_sync_byte,
|
||
- u8 sync_byte_value)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- if (check_sync_byte)
|
||
- reg |= (0x1 << SRC_CONFIG_CHECK_SYNC_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << SRC_CONFIG_CHECK_SYNC_OFFS);
|
||
-
|
||
- reg &= ~(0xFF << SRC_CONFIG_SYNC_BYTE_OFFS);
|
||
- reg |= (sync_byte_value << SRC_CONFIG_SYNC_BYTE_OFFS);
|
||
-
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_sync_byte_config_set);
|
||
-
|
||
-/**
|
||
- * tspp2_src_sync_byte_config_get() - Get source sync byte configuration.
|
||
- *
|
||
- * @src_handle: Source handle.
|
||
- * @check_sync_byte: Check TS packet sync byte indication.
|
||
- * @sync_byte_value: Sync byte value.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_sync_byte_config_get(u32 src_handle,
|
||
- int *check_sync_byte,
|
||
- u8 *sync_byte_value)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!check_sync_byte || !sync_byte_value) {
|
||
- pr_err("%s: NULL pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- *check_sync_byte = (reg >> SRC_CONFIG_CHECK_SYNC_OFFS) & 0x1;
|
||
- *sync_byte_value = (reg >> SRC_CONFIG_SYNC_BYTE_OFFS) & 0xFF;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_sync_byte_config_get);
|
||
-
|
||
-/**
|
||
- * tspp2_src_scrambling_config_set() - Set source scrambling configuration.
|
||
- *
|
||
- * @src_handle: Source to configure.
|
||
- * @cfg: Scrambling configuration to set.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_scrambling_config_set(u32 src_handle,
|
||
- const struct tspp2_src_scrambling_config *cfg)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!cfg) {
|
||
- pr_err("%s: NULL pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- /* Clear all scrambling configuration bits before setting them */
|
||
- reg &= ~(0x3 << SRC_CONFIG_SCRAMBLING0_OFFS);
|
||
- reg &= ~(0x3 << SRC_CONFIG_SCRAMBLING1_OFFS);
|
||
- reg &= ~(0x3 << SRC_CONFIG_SCRAMBLING2_OFFS);
|
||
- reg &= ~(0x3 << SRC_CONFIG_SCRAMBLING3_OFFS);
|
||
- reg &= ~(0x3 << SRC_CONFIG_SCRAMBLING_MONITOR_OFFS);
|
||
-
|
||
- reg |= (cfg->scrambling_0_ctrl << SRC_CONFIG_SCRAMBLING0_OFFS);
|
||
- reg |= (cfg->scrambling_1_ctrl << SRC_CONFIG_SCRAMBLING1_OFFS);
|
||
- reg |= (cfg->scrambling_2_ctrl << SRC_CONFIG_SCRAMBLING2_OFFS);
|
||
- reg |= (cfg->scrambling_3_ctrl << SRC_CONFIG_SCRAMBLING3_OFFS);
|
||
- reg |= (cfg->scrambling_bits_monitoring <<
|
||
- SRC_CONFIG_SCRAMBLING_MONITOR_OFFS);
|
||
-
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- src->scrambling_bits_monitoring = cfg->scrambling_bits_monitoring;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_scrambling_config_set);
|
||
-
|
||
-/**
|
||
- * tspp2_src_scrambling_config_get() - Get source scrambling configuration.
|
||
- *
|
||
- * @src_handle: Source handle.
|
||
- * @cfg: Scrambling configuration.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_scrambling_config_get(u32 src_handle,
|
||
- struct tspp2_src_scrambling_config *cfg)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!cfg) {
|
||
- pr_err("%s: NULL pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- cfg->scrambling_0_ctrl = ((reg >> SRC_CONFIG_SCRAMBLING0_OFFS) & 0x3);
|
||
- cfg->scrambling_1_ctrl = ((reg >> SRC_CONFIG_SCRAMBLING1_OFFS) & 0x3);
|
||
- cfg->scrambling_2_ctrl = ((reg >> SRC_CONFIG_SCRAMBLING2_OFFS) & 0x3);
|
||
- cfg->scrambling_3_ctrl = ((reg >> SRC_CONFIG_SCRAMBLING3_OFFS) & 0x3);
|
||
- cfg->scrambling_bits_monitoring =
|
||
- ((reg >> SRC_CONFIG_SCRAMBLING_MONITOR_OFFS) & 0x3);
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_scrambling_config_get);
|
||
-
|
||
-/**
|
||
- * tspp2_src_packet_format_set() - Set source packet size and format.
|
||
- *
|
||
- * @src_handle: Source to configure.
|
||
- * @format: Packet format.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_packet_format_set(u32 src_handle,
|
||
- enum tspp2_packet_format format)
|
||
-{
|
||
- int ret;
|
||
- u32 reg = 0;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (src->input == TSPP2_INPUT_MEMORY) {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
-
|
||
- reg &= ~(0x1 << MEM_INPUT_SRC_CONFIG_STAMP_SUFFIX_OFFS);
|
||
- reg &= ~(0x1 << MEM_INPUT_SRC_CONFIG_STAMP_EN_OFFS);
|
||
-
|
||
- switch (format) {
|
||
- case TSPP2_PACKET_FORMAT_188_RAW:
|
||
- /* We do not need to set any bit */
|
||
- break;
|
||
- case TSPP2_PACKET_FORMAT_192_HEAD:
|
||
- reg |= (0x1 << MEM_INPUT_SRC_CONFIG_STAMP_EN_OFFS);
|
||
- break;
|
||
- case TSPP2_PACKET_FORMAT_192_TAIL:
|
||
- reg |= (0x1 << MEM_INPUT_SRC_CONFIG_STAMP_EN_OFFS);
|
||
- reg |= (0x1 << MEM_INPUT_SRC_CONFIG_STAMP_SUFFIX_OFFS);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Unknown packet format\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- }
|
||
- src->pkt_format = format;
|
||
-
|
||
- /* Update source's input pipe threshold if needed */
|
||
- if (src->input_pipe) {
|
||
- if (src->pkt_format == TSPP2_PACKET_FORMAT_188_RAW)
|
||
- src->input_pipe->threshold = 188;
|
||
- else
|
||
- src->input_pipe->threshold = 192;
|
||
-
|
||
- writel_relaxed(src->input_pipe->threshold,
|
||
- src->input_pipe->device->base +
|
||
- TSPP2_PIPE_THRESH_CONFIG(src->input_pipe->hw_index));
|
||
- }
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_packet_format_set);
|
||
-
|
||
-/**
|
||
- * tspp2_src_pipe_attach() - Attach a pipe to a source.
|
||
- *
|
||
- * @src_handle: Source to attach the pipe to.
|
||
- * @pipe_handle: Pipe to attach to the source.
|
||
- * @cfg: For output pipes - the pipe's pull mode parameters.
|
||
- * It is not allowed to pass NULL for output pipes.
|
||
- * For input pipes this is irrelevant and the caller can
|
||
- * pass NULL.
|
||
- *
|
||
- * This function attaches a given pipe to a given source.
|
||
- * The pipe's mode (input or output) was set when the pipe was opened.
|
||
- * An input pipe can be attached to a single source (with memory input).
|
||
- * A source can have multiple output pipes attached, and an output pipe can
|
||
- * be attached to multiple sources.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_pipe_attach(u32 src_handle,
|
||
- u32 pipe_handle,
|
||
- const struct tspp2_pipe_pull_mode_params *cfg)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
- struct tspp2_output_pipe *output_pipe = NULL;
|
||
- u32 reg;
|
||
-
|
||
- if (!src || !pipe) {
|
||
- pr_err("%s: Invalid source or pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
- if ((pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_OUTPUT) && (cfg == NULL)) {
|
||
- pr_err("%s: Invalid pull mode parameters\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_INPUT) {
|
||
- if (src->input_pipe != NULL) {
|
||
- pr_err("%s: Source already has an input pipe attached\n",
|
||
- __func__);
|
||
- goto err_inval;
|
||
- }
|
||
- if (pipe->ref_cnt > 0) {
|
||
- pr_err(
|
||
- "%s: Pipe %u is already attached to a source. An input pipe can only be attached once\n",
|
||
- __func__, pipe_handle);
|
||
- goto err_inval;
|
||
- }
|
||
- /*
|
||
- * Input pipe threshold is determined according to the
|
||
- * source's packet size.
|
||
- */
|
||
- if (src->pkt_format == TSPP2_PACKET_FORMAT_188_RAW)
|
||
- pipe->threshold = 188;
|
||
- else
|
||
- pipe->threshold = 192;
|
||
-
|
||
- src->input_pipe = pipe;
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- reg &= ~(0x1F << MEM_INPUT_SRC_CONFIG_INPUT_PIPE_OFFS);
|
||
- reg |= (pipe->hw_index << MEM_INPUT_SRC_CONFIG_INPUT_PIPE_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- } else {
|
||
- list_for_each_entry(output_pipe,
|
||
- &src->output_pipe_list, link) {
|
||
- if (output_pipe->pipe == pipe) {
|
||
- pr_err(
|
||
- "%s: Output pipe %u is already attached to source %u\n",
|
||
- __func__, pipe_handle, src_handle);
|
||
- goto err_inval;
|
||
- }
|
||
- }
|
||
- output_pipe = kmalloc(sizeof(struct tspp2_output_pipe),
|
||
- GFP_KERNEL);
|
||
- if (!output_pipe) {
|
||
- pr_err("%s: No memory to save output pipe\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
- output_pipe->pipe = pipe;
|
||
- pipe->threshold = (cfg->threshold & 0xFFFF);
|
||
- list_add_tail(&output_pipe->link, &src->output_pipe_list);
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_DEST_PIPES(src->hw_index));
|
||
- if (cfg->is_stalling)
|
||
- reg |= (0x1 << pipe->hw_index);
|
||
- else
|
||
- reg &= ~(0x1 << pipe->hw_index);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_SRC_DEST_PIPES(src->hw_index));
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(pipe->device->base +
|
||
- TSPP2_PIPE_THRESH_CONFIG(pipe->hw_index));
|
||
- if ((pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_OUTPUT) &&
|
||
- (pipe->ref_cnt > 0) && (pipe->threshold != (reg & 0xFFFF))) {
|
||
- pr_warn("%s: overwriting output pipe threshold\n", __func__);
|
||
- }
|
||
-
|
||
- writel_relaxed(pipe->threshold, pipe->device->base +
|
||
- TSPP2_PIPE_THRESH_CONFIG(pipe->hw_index));
|
||
-
|
||
- pipe->ref_cnt++;
|
||
- src->num_associated_pipes++;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_inval:
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_pipe_attach);
|
||
-
|
||
-/**
|
||
- * tspp2_src_pipe_detach() - Detach a pipe from a source.
|
||
- *
|
||
- * @src_handle: Source to detach the pipe from.
|
||
- * @pipe_handle: Pipe to detach from the source.
|
||
- *
|
||
- * Detaches a pipe from a source. The given pipe should have been previously
|
||
- * attached to this source as either an input pipe or an output pipe.
|
||
- * Note: there is no checking if this pipe is currently defined as the output
|
||
- * pipe of any operation!
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_pipe_detach(u32 src_handle, u32 pipe_handle)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
- struct tspp2_output_pipe *output_pipe = NULL;
|
||
- int found = 0;
|
||
- u32 reg;
|
||
-
|
||
- if (!src || !pipe) {
|
||
- pr_err("%s: Invalid source or pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&src->device->mutex);
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (pipe->cfg.pipe_mode == TSPP2_SRC_PIPE_INPUT) {
|
||
- if (src->input_pipe != pipe) {
|
||
- pr_err(
|
||
- "%s: Input pipe %u is not attached to source %u\n",
|
||
- __func__, pipe_handle, src_handle);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- writel_relaxed(0xFFFF, src->input_pipe->device->base +
|
||
- TSPP2_PIPE_THRESH_CONFIG(src->input_pipe->hw_index));
|
||
-
|
||
- if (src->enabled) {
|
||
- pr_warn("%s: Detaching input pipe from an active memory source\n",
|
||
- __func__);
|
||
- }
|
||
- /*
|
||
- * Note: not updating TSPP2_MEM_INPUT_SRC_CONFIG to reflect
|
||
- * this pipe is detached, since there is no invalid value we
|
||
- * can write instead. tspp2_src_pipe_attach() already takes
|
||
- * care of zeroing the relevant bit-field before writing the
|
||
- * new pipe nummber.
|
||
- */
|
||
-
|
||
- src->input_pipe = NULL;
|
||
- } else {
|
||
- list_for_each_entry(output_pipe,
|
||
- &src->output_pipe_list, link) {
|
||
- if (output_pipe->pipe == pipe) {
|
||
- found = 1;
|
||
- break;
|
||
- }
|
||
- }
|
||
- if (found) {
|
||
- list_del(&output_pipe->link);
|
||
- kfree(output_pipe);
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_SRC_DEST_PIPES(src->hw_index));
|
||
- reg &= ~(0x1 << pipe->hw_index);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_SRC_DEST_PIPES(src->hw_index));
|
||
- if (pipe->ref_cnt == 1) {
|
||
- writel_relaxed(0xFFFF, pipe->device->base +
|
||
- TSPP2_PIPE_THRESH_CONFIG(
|
||
- pipe->hw_index));
|
||
- }
|
||
- } else {
|
||
- pr_err("%s: Output pipe %u is not attached to source %u\n",
|
||
- __func__, pipe_handle, src_handle);
|
||
- goto err_inval;
|
||
- }
|
||
- }
|
||
- pipe->ref_cnt--;
|
||
- src->num_associated_pipes--;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_inval:
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_pipe_detach);
|
||
-
|
||
-/**
|
||
- * tspp2_src_enable() - Enable source.
|
||
- *
|
||
- * @src_handle: Source to enable.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_enable(u32 src_handle)
|
||
-{
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- u32 reg;
|
||
- int ret;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (src->enabled) {
|
||
- pr_warn("%s: Source already enabled\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return 0;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Memory sources require their input pipe to be configured
|
||
- * before enabling the source.
|
||
- */
|
||
- if ((src->input == TSPP2_INPUT_MEMORY) && (src->input_pipe == NULL)) {
|
||
- pr_err("%s: A memory source must have an input pipe attached before enabling the source",
|
||
- __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (src->device->num_enabled_sources == 0) {
|
||
- ret = tspp2_clock_start(src->device);
|
||
- if (ret) {
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return ret;
|
||
- }
|
||
- __pm_stay_awake(&src->device->wakeup_src);
|
||
- }
|
||
-
|
||
- if ((src->input == TSPP2_INPUT_TSIF0) ||
|
||
- (src->input == TSPP2_INPUT_TSIF1)) {
|
||
- tspp2_tsif_start(&src->device->tsif_devices[src->input]);
|
||
-
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- reg |= (0x1 << TSIF_INPUT_SRC_CONFIG_INPUT_EN_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- } else {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- reg |= (0x1 << MEM_INPUT_SRC_CONFIG_INPUT_EN_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- }
|
||
-
|
||
- src->enabled = 1;
|
||
- src->device->num_enabled_sources++;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_enable);
|
||
-
|
||
-/**
|
||
- * tspp2_src_disable() - Disable source.
|
||
- *
|
||
- * @src_handle: Source to disable.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_disable(u32 src_handle)
|
||
-{
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- int ret;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&src->device->mutex);
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- ret = tspp2_src_disable_internal(src);
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- if (!ret)
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_disable);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_ops_clear() - Clear filter operations database and HW
|
||
- *
|
||
- * @filter: The filter to work on.
|
||
- */
|
||
-static void tspp2_filter_ops_clear(struct tspp2_filter *filter)
|
||
-{
|
||
- int i;
|
||
-
|
||
- /* Set all filter operations in HW to Exit operation */
|
||
- for (i = 0; i < TSPP2_MAX_OPS_PER_FILTER; i++) {
|
||
- writel_relaxed(TSPP2_OPCODE_EXIT, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, i));
|
||
- }
|
||
- memset(filter->operations, 0,
|
||
- (sizeof(struct tspp2_operation) * TSPP2_MAX_OPS_PER_FILTER));
|
||
- filter->num_user_operations = 0;
|
||
- filter->indexing_op_set = 0;
|
||
- filter->raw_op_with_indexing = 0;
|
||
- filter->pes_analysis_op_set = 0;
|
||
- filter->raw_op_set = 0;
|
||
- filter->pes_tx_op_set = 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_context_reset() - Reset filter context and release it.
|
||
- *
|
||
- * @filter: The filter to work on.
|
||
- */
|
||
-static void tspp2_filter_context_reset(struct tspp2_filter *filter)
|
||
-{
|
||
- /* Reset this filter's context. Each register handles 32 contexts */
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_TSP_CONTEXT_RESET(filter->context >> 5));
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_PES_CONTEXT_RESET(filter->context >> 5));
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_INDEXING_CONTEXT_RESET(filter->context >> 5));
|
||
-
|
||
- writel_relaxed(0, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY1(filter->hw_index));
|
||
-
|
||
- /* Release context */
|
||
- filter->device->contexts[filter->context] = 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_sw_reset() - Reset filter SW fields helper function.
|
||
- *
|
||
- * @filter: The filter to work on.
|
||
- */
|
||
-static void tspp2_filter_sw_reset(struct tspp2_filter *filter)
|
||
-{
|
||
- unsigned long flags;
|
||
- /*
|
||
- * All fields are cleared when opening a filter. Still it is important
|
||
- * to reset some of the fields here, specifically to set opened to 0 and
|
||
- * also to set the callback to NULL.
|
||
- */
|
||
- filter->opened = 0;
|
||
- filter->src = NULL;
|
||
- filter->batch = NULL;
|
||
- filter->context = 0;
|
||
- filter->hw_index = 0;
|
||
- filter->pid_value = 0;
|
||
- filter->mask = 0;
|
||
- spin_lock_irqsave(&filter->device->spinlock, flags);
|
||
- filter->event_callback = NULL;
|
||
- filter->event_cookie = NULL;
|
||
- filter->event_bitmask = 0;
|
||
- spin_unlock_irqrestore(&filter->device->spinlock, flags);
|
||
- filter->enabled = 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_src_batch_set() - Set/clear a filter batch to/from a source.
|
||
- *
|
||
- * @src: The source to work on.
|
||
- * @batch_id: The batch to set/clear.
|
||
- * @set: Set/clear flag.
|
||
- */
|
||
-static void tspp2_src_batch_set(struct tspp2_src *src, u8 batch_id, int set)
|
||
-{
|
||
- u32 reg = 0;
|
||
-
|
||
- if (src->input == TSPP2_INPUT_MEMORY) {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- if (set)
|
||
- reg |= ((1 << batch_id) <<
|
||
- MEM_INPUT_SRC_CONFIG_BATCHES_OFFS);
|
||
- else
|
||
- reg &= ~((1 << batch_id) <<
|
||
- MEM_INPUT_SRC_CONFIG_BATCHES_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_MEM_INPUT_SRC_CONFIG(src->hw_index));
|
||
- } else {
|
||
- reg = readl_relaxed(src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- if (set)
|
||
- reg |= ((1 << batch_id) <<
|
||
- TSIF_INPUT_SRC_CONFIG_BATCHES_OFFS);
|
||
- else
|
||
- reg &= ~((1 << batch_id) <<
|
||
- TSIF_INPUT_SRC_CONFIG_BATCHES_OFFS);
|
||
- writel_relaxed(reg, src->device->base +
|
||
- TSPP2_TSIF_INPUT_SRC_CONFIG(src->input));
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_src_filters_clear() - Clear all filters from a source.
|
||
- *
|
||
- * @src_handle: Source to clear all filters from.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_filters_clear(u32 src_handle)
|
||
-{
|
||
- int ret;
|
||
- int i;
|
||
- struct tspp2_filter *filter = NULL;
|
||
- struct tspp2_filter *tmp_filter;
|
||
- struct tspp2_filter_batch *batch = NULL;
|
||
- struct tspp2_filter_batch *tmp_batch;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&src->device->mutex);
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Go over filters in source, disable them, clear their operations,
|
||
- * "close" them (similar to tspp2_filter_close function but simpler).
|
||
- * No need to worry about cases of reserved filter, so just clear
|
||
- * filters HW- and SW-wise. Then update source's filters and batches
|
||
- * lists and numbers. Simple :)
|
||
- */
|
||
- list_for_each_entry_safe(filter, tmp_filter, &src->filters_list, link) {
|
||
- /* Disable filter */
|
||
- writel_relaxed(0, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
- /* Clear filter operations in HW as well as related SW fields */
|
||
- tspp2_filter_ops_clear(filter);
|
||
- /* Reset filter context-based counters */
|
||
- tspp2_filter_counters_reset(filter->device, filter->context);
|
||
- /* Reset filter context and release it back to the device */
|
||
- tspp2_filter_context_reset(filter);
|
||
- /* Reset filter SW fields */
|
||
- tspp2_filter_sw_reset(filter);
|
||
-
|
||
- list_del(&filter->link);
|
||
- }
|
||
-
|
||
- list_for_each_entry_safe(batch, tmp_batch, &src->batches_list, link) {
|
||
- tspp2_src_batch_set(src, batch->batch_id, 0);
|
||
- for (i = 0; i < TSPP2_FILTERS_PER_BATCH; i++)
|
||
- batch->hw_filters[i] = 0;
|
||
- batch->src = NULL;
|
||
- list_del(&batch->link);
|
||
- }
|
||
-
|
||
- src->num_associated_batches = 0;
|
||
- src->num_associated_filters = 0;
|
||
- src->reserved_filter_hw_index = 0;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_filters_clear);
|
||
-
|
||
-/* Filters and Operations API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_filter_open() - Open a new filter and add it to a source.
|
||
- *
|
||
- * @src_handle: Source to add the new filter to.
|
||
- * @pid: Filter's 13-bit PID value.
|
||
- * @mask: Filter's 13-bit mask. Note it is highly recommended
|
||
- * to use a full bit mask of 0x1FFF, so the filter
|
||
- * operates on a unique PID.
|
||
- * @filter_handle: Opened filter handle.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_open(u32 src_handle, u16 pid, u16 mask, u32 *filter_handle)
|
||
-{
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- struct tspp2_filter_batch *batch;
|
||
- struct tspp2_filter *filter = NULL;
|
||
- u16 hw_idx;
|
||
- int i;
|
||
- u32 reg = 0;
|
||
- int found = 0;
|
||
- int ret;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!filter_handle) {
|
||
- pr_err("%s: Invalid filter handle pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if ((pid & ~0x1FFF) || (mask & ~0x1FFF)) {
|
||
- pr_err("%s: Invalid PID or mask values (13 bits available)\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Find an available filter object in the device's filters database */
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_FILTERS; i++)
|
||
- if (!src->device->filters[i].opened)
|
||
- break;
|
||
- if (i == TSPP2_NUM_AVAIL_FILTERS) {
|
||
- pr_err("%s: No available filters\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
- filter = &src->device->filters[i];
|
||
-
|
||
- /* Find an available context. Each new filter needs a unique context */
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_CONTEXTS; i++)
|
||
- if (!src->device->contexts[i])
|
||
- break;
|
||
- if (i == TSPP2_NUM_AVAIL_CONTEXTS) {
|
||
- pr_err("%s: No available filters\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
- src->device->contexts[i] = 1;
|
||
- filter->context = i;
|
||
-
|
||
- if (src->num_associated_batches) {
|
||
- /*
|
||
- * Look for an available HW filter among the batches
|
||
- * already associated with this source.
|
||
- */
|
||
- list_for_each_entry(batch, &src->batches_list, link) {
|
||
- for (i = 0; i < TSPP2_FILTERS_PER_BATCH; i++) {
|
||
- hw_idx = (batch->batch_id *
|
||
- TSPP2_FILTERS_PER_BATCH) + i;
|
||
- if ((hw_idx != src->reserved_filter_hw_index) &&
|
||
- (batch->hw_filters[i] == 0))
|
||
- break;
|
||
- }
|
||
- if (i < TSPP2_FILTERS_PER_BATCH) {
|
||
- /* Found an available HW filter */
|
||
- batch->hw_filters[i] = 1;
|
||
- found = 1;
|
||
- break;
|
||
- }
|
||
- }
|
||
- }
|
||
-
|
||
- if (!found) {
|
||
- /* Either the source did not have any associated batches,
|
||
- * or we could not find an available HW filter in any of
|
||
- * the source's batches. In any case, we need to find a new
|
||
- * batch. Then we use the first filter in this batch.
|
||
- */
|
||
- for (i = 0; i < TSPP2_NUM_BATCHES; i++) {
|
||
- if (!src->device->batches[i].src) {
|
||
- src->device->batches[i].src = src;
|
||
- batch = &src->device->batches[i];
|
||
- batch->hw_filters[0] = 1;
|
||
- hw_idx = (batch->batch_id *
|
||
- TSPP2_FILTERS_PER_BATCH);
|
||
- break;
|
||
- }
|
||
- }
|
||
- if (i == TSPP2_NUM_BATCHES) {
|
||
- pr_err("%s: No available filters\n", __func__);
|
||
- src->device->contexts[filter->context] = 0;
|
||
- filter->context = 0;
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ENOMEM;
|
||
- }
|
||
-
|
||
- tspp2_src_batch_set(src, batch->batch_id, 1);
|
||
-
|
||
- list_add_tail(&batch->link, &src->batches_list);
|
||
-
|
||
- /* Update reserved filter index only when needed */
|
||
- if (src->num_associated_batches == 0) {
|
||
- src->reserved_filter_hw_index =
|
||
- (batch->batch_id * TSPP2_FILTERS_PER_BATCH) +
|
||
- TSPP2_FILTERS_PER_BATCH - 1;
|
||
- }
|
||
- src->num_associated_batches++;
|
||
- }
|
||
-
|
||
- filter->opened = 1;
|
||
- filter->src = src;
|
||
- filter->batch = batch;
|
||
- filter->hw_index = hw_idx;
|
||
- filter->pid_value = pid;
|
||
- filter->mask = mask;
|
||
- filter->indexing_table_id = 0;
|
||
- tspp2_filter_ops_clear(filter);
|
||
- filter->event_callback = NULL;
|
||
- filter->event_cookie = NULL;
|
||
- filter->event_bitmask = 0;
|
||
- filter->enabled = 0;
|
||
- /* device back-pointer is already initialized, always remains valid */
|
||
-
|
||
- list_add_tail(&filter->link, &src->filters_list);
|
||
- src->num_associated_filters++;
|
||
-
|
||
- /* Reset filter context-based counters */
|
||
- tspp2_filter_counters_reset(filter->device, filter->context);
|
||
-
|
||
- /* Reset this filter's context */
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_TSP_CONTEXT_RESET(filter->context >> 5));
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_PES_CONTEXT_RESET(filter->context >> 5));
|
||
- writel_relaxed((0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_INDEXING_CONTEXT_RESET(filter->context >> 5));
|
||
-
|
||
- /* Write PID and mask */
|
||
- reg = ((pid << FILTER_ENTRY0_PID_OFFS) |
|
||
- (mask << FILTER_ENTRY0_MASK_OFFS));
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- writel_relaxed((filter->context << FILTER_ENTRY1_CONTEXT_OFFS),
|
||
- filter->device->base + TSPP2_FILTER_ENTRY1(filter->hw_index));
|
||
-
|
||
- *filter_handle = (u32)filter;
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_open);
|
||
-
|
||
-/**
|
||
- * tspp2_hw_filters_in_batch() - Check for used HW filters in a batch.
|
||
- *
|
||
- * @batch: The filter batch to check.
|
||
- *
|
||
- * Helper function to check if there are any HW filters used on this batch.
|
||
- *
|
||
- * Return 1 if found a used filter in this batch, 0 otherwise.
|
||
- */
|
||
-static inline int tspp2_hw_filters_in_batch(struct tspp2_filter_batch *batch)
|
||
-{
|
||
- int i;
|
||
-
|
||
- for (i = 0; i < TSPP2_FILTERS_PER_BATCH; i++)
|
||
- if (batch->hw_filters[i] == 1)
|
||
- return 1;
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_close() - Close a filter.
|
||
- *
|
||
- * @filter_handle: Filter to close.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_close(u32 filter_handle)
|
||
-{
|
||
- int i;
|
||
- int ret;
|
||
- struct tspp2_device *device;
|
||
- struct tspp2_src *src = NULL;
|
||
- struct tspp2_filter_batch *batch = NULL;
|
||
- struct tspp2_filter_batch *tmp_batch;
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = filter->device;
|
||
-
|
||
- ret = pm_runtime_get_sync(device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&device->mutex);
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter already closed\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (filter->num_user_operations)
|
||
- pr_warn("%s: Closing filters that has %d operations\n",
|
||
- __func__, filter->num_user_operations);
|
||
-
|
||
- /* Disable filter */
|
||
- writel_relaxed(0, device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- /* Clear filter operations in HW as well as related SW fields */
|
||
- tspp2_filter_ops_clear(filter);
|
||
-
|
||
- /* Reset filter context-based counters */
|
||
- tspp2_filter_counters_reset(device, filter->context);
|
||
-
|
||
- /* Reset filter context and release it back to the device */
|
||
- tspp2_filter_context_reset(filter);
|
||
-
|
||
- /* Mark filter as unused in batch */
|
||
- filter->batch->hw_filters[(filter->hw_index -
|
||
- (filter->batch->batch_id * TSPP2_FILTERS_PER_BATCH))] = 0;
|
||
-
|
||
- /* Remove filter from source */
|
||
- list_del(&filter->link);
|
||
- filter->src->num_associated_filters--;
|
||
-
|
||
- /* We may need to update the reserved filter for this source.
|
||
- * Cases to handle:
|
||
- * 1. This is the last filter on this source.
|
||
- * 2. This is the last filter on this batch + reserved filter is not on
|
||
- * this batch.
|
||
- * 3. This is the last filter on this batch + reserved filter is on this
|
||
- * batch. Can possibly move reserved filter to another batch if space is
|
||
- * available.
|
||
- * 4. This is not the last filter on this batch. The reserved filter may
|
||
- * be the only one taking another batch and may be moved to this batch
|
||
- * to save space.
|
||
- */
|
||
-
|
||
- src = filter->src;
|
||
- /*
|
||
- * Case #1: this could be the last filter associated with this source.
|
||
- * If this is the case, we can release the batch too. We don't care
|
||
- * about the reserved HW filter index, since there are no more filters.
|
||
- */
|
||
- if (src->num_associated_filters == 0) {
|
||
- filter->batch->src = NULL;
|
||
- list_del(&filter->batch->link);
|
||
- src->num_associated_batches--;
|
||
- tspp2_src_batch_set(src, filter->batch->batch_id, 0);
|
||
- src->reserved_filter_hw_index = 0;
|
||
- goto filter_clear;
|
||
- }
|
||
-
|
||
- /*
|
||
- * If this is the last filter that was used in this batch, we may be
|
||
- * able to release this entire batch. However, we have to make sure the
|
||
- * reserved filter is not in this batch. If it is, we may find a place
|
||
- * for it in another batch in this source.
|
||
- */
|
||
- if (!tspp2_hw_filters_in_batch(filter->batch)) {
|
||
- /* There are no more used filters on this batch */
|
||
- if ((src->reserved_filter_hw_index <
|
||
- (filter->batch->batch_id * TSPP2_FILTERS_PER_BATCH)) ||
|
||
- (src->reserved_filter_hw_index >=
|
||
- ((filter->batch->batch_id * TSPP2_FILTERS_PER_BATCH) +
|
||
- TSPP2_FILTERS_PER_BATCH))) {
|
||
- /* Case #2: the reserved filter is not on this batch */
|
||
- filter->batch->src = NULL;
|
||
- list_del(&filter->batch->link);
|
||
- src->num_associated_batches--;
|
||
- tspp2_src_batch_set(src, filter->batch->batch_id, 0);
|
||
- } else {
|
||
- /*
|
||
- * Case #3: see if we can "move" the reserved filter to
|
||
- * a different batch.
|
||
- */
|
||
- list_for_each_entry_safe(batch, tmp_batch,
|
||
- &src->batches_list, link) {
|
||
- if (batch == filter->batch)
|
||
- continue;
|
||
-
|
||
- for (i = 0; i < TSPP2_FILTERS_PER_BATCH; i++) {
|
||
- if (batch->hw_filters[i] == 0) {
|
||
- src->reserved_filter_hw_index =
|
||
- (batch->batch_id *
|
||
- TSPP2_FILTERS_PER_BATCH)
|
||
- + i;
|
||
-
|
||
- filter->batch->src = NULL;
|
||
- list_del(&filter->batch->link);
|
||
- src->num_associated_batches--;
|
||
- tspp2_src_batch_set(src,
|
||
- filter->batch->batch_id,
|
||
- 0);
|
||
- goto filter_clear;
|
||
- }
|
||
- }
|
||
- }
|
||
- }
|
||
- } else {
|
||
- /* Case #4: whenever we remove a filter, there is always a
|
||
- * chance that the reserved filter was the only filter used on a
|
||
- * different batch. So now this is a good opportunity to check
|
||
- * if we can release that batch and use the index of the filter
|
||
- * we're freeing instead.
|
||
- */
|
||
- list_for_each_entry_safe(batch, tmp_batch,
|
||
- &src->batches_list, link) {
|
||
- if (((src->reserved_filter_hw_index >=
|
||
- (batch->batch_id * TSPP2_FILTERS_PER_BATCH)) &&
|
||
- (src->reserved_filter_hw_index <
|
||
- (batch->batch_id * TSPP2_FILTERS_PER_BATCH +
|
||
- TSPP2_FILTERS_PER_BATCH))) &&
|
||
- !tspp2_hw_filters_in_batch(batch)) {
|
||
- src->reserved_filter_hw_index =
|
||
- filter->hw_index;
|
||
- batch->src = NULL;
|
||
- list_del(&batch->link);
|
||
- src->num_associated_batches--;
|
||
- tspp2_src_batch_set(src, batch->batch_id, 0);
|
||
- break;
|
||
- }
|
||
- }
|
||
- }
|
||
-
|
||
-filter_clear:
|
||
- tspp2_filter_sw_reset(filter);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(device->dev);
|
||
- pm_runtime_put_autosuspend(device->dev);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_close);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_enable() - Enable a filter.
|
||
- *
|
||
- * @filter_handle: Filter to enable.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_enable(u32 filter_handle)
|
||
-{
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- u32 reg;
|
||
- int ret;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&filter->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (filter->enabled) {
|
||
- pr_warn("%s: Filter already enabled\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return 0;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
- reg |= (0x1 << FILTER_ENTRY0_EN_OFFS);
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- filter->enabled = 1;
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_enable);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_disable() - Disable a filter.
|
||
- *
|
||
- * @filter_handle: Filter to disable.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_disable(u32 filter_handle)
|
||
-{
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- u32 reg;
|
||
- int ret;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&filter->device->mutex);
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!filter->enabled) {
|
||
- pr_warn("%s: Filter already disabled\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return 0;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
- reg &= ~(0x1 << FILTER_ENTRY0_EN_OFFS);
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- /*
|
||
- * HW requires we wait for up to 2ms here before closing the pipes
|
||
- * used by this filter
|
||
- */
|
||
- udelay(TSPP2_HW_DELAY_USEC);
|
||
-
|
||
- filter->enabled = 0;
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_disable);
|
||
-
|
||
-/**
|
||
- * tspp2_pes_analysis_op_write() - Write a PES Analysis operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_pes_analysis_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
-
|
||
- if (filter->mask != TSPP2_UNIQUE_PID_MASK) {
|
||
- pr_err(
|
||
- "%s: A filter with a PES Analysis operation must handle a unique PID\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Bits[19:6] = 0, Bit[5] = Source,
|
||
- * Bit[4] = Skip, Bits[3:0] = Opcode
|
||
- */
|
||
- reg |= TSPP2_OPCODE_PES_ANALYSIS;
|
||
- if (op->params.pes_analysis.skip_ts_errs)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.pes_analysis.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- filter->pes_analysis_op_set = 1;
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_raw_tx_op_write() - Write a RAW Transmit operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_raw_tx_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
- int timestamp = 0;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)
|
||
- op->params.raw_transmit.output_pipe_handle;
|
||
-
|
||
- if (!pipe || !pipe->opened) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Bits[19:16] = 0, Bit[15] = Support Indexing,
|
||
- * Bit[14] = Timestamp position,
|
||
- * Bits[13:12] = Timestamp mode,
|
||
- * Bits[11:6] = Output pipe, Bit[5] = Source,
|
||
- * Bit[4] = Skip, Bits[3:0] = Opcode
|
||
- */
|
||
- reg |= TSPP2_OPCODE_RAW_TRANSMIT;
|
||
- if (op->params.raw_transmit.skip_ts_errs)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.raw_transmit.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- reg |= ((pipe->hw_index & 0x3F) << 6);
|
||
-
|
||
- switch (op->params.raw_transmit.timestamp_mode) {
|
||
- case TSPP2_OP_TIMESTAMP_NONE:
|
||
- /* nothing to do, keep bits value as 0 */
|
||
- break;
|
||
- case TSPP2_OP_TIMESTAMP_ZERO:
|
||
- reg |= (0x1 << 12);
|
||
- timestamp = 1;
|
||
- break;
|
||
- case TSPP2_OP_TIMESTAMP_STC:
|
||
- reg |= (0x2 << 12);
|
||
- timestamp = 1;
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Invalid timestamp mode\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (timestamp && op->params.raw_transmit.timestamp_position ==
|
||
- TSPP2_PACKET_FORMAT_188_RAW) {
|
||
- pr_err("%s: Invalid timestamp position\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (op->params.raw_transmit.timestamp_position ==
|
||
- TSPP2_PACKET_FORMAT_192_TAIL)
|
||
- reg |= (0x1 << 14);
|
||
-
|
||
- if (op->params.raw_transmit.support_indexing) {
|
||
- if (filter->raw_op_with_indexing) {
|
||
- pr_err(
|
||
- "%s: Only one Raw Transmit operation per filter can support HW indexing\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- filter->raw_op_with_indexing = 1;
|
||
- reg |= (0x1 << 15);
|
||
- }
|
||
-
|
||
- filter->raw_op_set = 1;
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_pes_tx_op_write() - Write a PES Transmit operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_pes_tx_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
- struct tspp2_pipe *payload_pipe = (struct tspp2_pipe *)
|
||
- op->params.pes_transmit.output_pipe_handle;
|
||
- struct tspp2_pipe *header_pipe;
|
||
-
|
||
- if (!payload_pipe || !payload_pipe->opened) {
|
||
- pr_err("%s: Invalid payload pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!filter->pes_analysis_op_set) {
|
||
- pr_err(
|
||
- "%s: PES Analysys operation must precede any PES Transmit operation\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Bits[19:18] = 0, Bits[17:12] = PES Header output pipe,
|
||
- * Bits[11:6] = Output pipe, Bit[5] = Source,
|
||
- * Bit[4] = Attach STC and flags,
|
||
- * Bit[3] = Disable TX on PES discontinuity,
|
||
- * Bit[2] = Enable SW indexing, Bit[1] = Mode, Bit[0] = 0
|
||
- */
|
||
-
|
||
- if (op->params.pes_transmit.mode == TSPP2_OP_PES_TRANSMIT_FULL) {
|
||
- reg |= (0x1 << 1);
|
||
- } else {
|
||
- /* Separated PES mode requires another pipe */
|
||
- header_pipe = (struct tspp2_pipe *)
|
||
- op->params.pes_transmit.header_output_pipe_handle;
|
||
-
|
||
- if (!header_pipe || !header_pipe->opened) {
|
||
- pr_err("%s: Invalid header pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg |= ((header_pipe->hw_index & 0x3F) << 12);
|
||
- }
|
||
-
|
||
- if (op->params.pes_transmit.enable_sw_indexing) {
|
||
- if (!filter->raw_op_set) {
|
||
- pr_err(
|
||
- "%s: PES Transmit operation with SW indexing must be preceded by a Raw Transmit operation\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- reg |= (0x1 << 2);
|
||
- }
|
||
-
|
||
- if (op->params.pes_transmit.disable_tx_on_pes_discontinuity)
|
||
- reg |= (0x1 << 3);
|
||
-
|
||
- if (op->params.pes_transmit.attach_stc_flags)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.pes_transmit.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- reg |= ((payload_pipe->hw_index & 0x3F) << 6);
|
||
-
|
||
- filter->pes_tx_op_set = 1;
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_pcr_op_write() - Write a PCR Extraction operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_pcr_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)
|
||
- op->params.pcr_extraction.output_pipe_handle;
|
||
-
|
||
- if (!pipe || !pipe->opened) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!op->params.pcr_extraction.extract_pcr &&
|
||
- !op->params.pcr_extraction.extract_opcr &&
|
||
- !op->params.pcr_extraction.extract_splicing_point &&
|
||
- !op->params.pcr_extraction.extract_transport_private_data &&
|
||
- !op->params.pcr_extraction.extract_af_extension &&
|
||
- !op->params.pcr_extraction.extract_all_af) {
|
||
- pr_err("%s: Invalid extraction parameters\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Bits[19:18] = 0, Bit[17] = All AF, Bit[16] = AF Extension,
|
||
- * Bit[15] = Transport Priave Data, Bit[14] = Splicing Point,
|
||
- * Bit[13] = OPCR, Bit[12] = PCR, Bits[11:6] = Output pipe,
|
||
- * Bit[5] = Source, Bit[4] = Skip, Bits[3:0] = Opcode
|
||
- */
|
||
- reg |= TSPP2_OPCODE_PCR_EXTRACTION;
|
||
- if (op->params.pcr_extraction.skip_ts_errs)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.pcr_extraction.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- reg |= ((pipe->hw_index & 0x3F) << 6);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_pcr)
|
||
- reg |= (0x1 << 12);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_opcr)
|
||
- reg |= (0x1 << 13);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_splicing_point)
|
||
- reg |= (0x1 << 14);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_transport_private_data)
|
||
- reg |= (0x1 << 15);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_af_extension)
|
||
- reg |= (0x1 << 16);
|
||
-
|
||
- if (op->params.pcr_extraction.extract_all_af)
|
||
- reg |= (0x1 << 17);
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_cipher_op_write() - Write a Cipher operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_cipher_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
-
|
||
- /*
|
||
- * Bits[19:18] = 0, Bits[17:15] = Scrambling related,
|
||
- * Bit[14] = Mode, Bit[13] = Decrypt PES header,
|
||
- * Bits[12:7] = Key ladder index, Bit[6] = Destination,
|
||
- * Bit[5] = Source, Bit[4] = Skip, Bits[3:0] = Opcode
|
||
- */
|
||
-
|
||
- reg |= TSPP2_OPCODE_CIPHER;
|
||
- if (op->params.cipher.skip_ts_errs)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.cipher.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- if (op->params.cipher.output == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 6);
|
||
-
|
||
- reg |= ((op->params.cipher.key_ladder_index & 0x3F) << 7);
|
||
-
|
||
- if (op->params.cipher.mode == TSPP2_OP_CIPHER_ENCRYPT &&
|
||
- op->params.cipher.decrypt_pes_header) {
|
||
- pr_err("%s: Invalid parameters\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (op->params.cipher.decrypt_pes_header)
|
||
- reg |= (0x1 << 13);
|
||
-
|
||
- if (op->params.cipher.mode == TSPP2_OP_CIPHER_ENCRYPT)
|
||
- reg |= (0x1 << 14);
|
||
-
|
||
- switch (op->params.cipher.scrambling_mode) {
|
||
- case TSPP2_OP_CIPHER_AS_IS:
|
||
- reg |= (0x1 << 15);
|
||
- break;
|
||
- case TSPP2_OP_CIPHER_SET_SCRAMBLING_0:
|
||
- /* nothing to do, keep bits[17:16] as 0 */
|
||
- break;
|
||
- case TSPP2_OP_CIPHER_SET_SCRAMBLING_1:
|
||
- reg |= (0x1 << 16);
|
||
- break;
|
||
- case TSPP2_OP_CIPHER_SET_SCRAMBLING_2:
|
||
- reg |= (0x2 << 16);
|
||
- break;
|
||
- case TSPP2_OP_CIPHER_SET_SCRAMBLING_3:
|
||
- reg |= (0x3 << 16);
|
||
- break;
|
||
- default:
|
||
- pr_err("%s: Invalid scrambling mode\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_index_op_write() - Write an Indexing operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_index_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
- u32 filter_reg = 0;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)
|
||
- op->params.indexing.output_pipe_handle;
|
||
-
|
||
- if (!pipe || !pipe->opened) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Enforce Indexing related HW restrictions */
|
||
- if (filter->indexing_op_set) {
|
||
- pr_err(
|
||
- "%s: Only one indexing operation supported per filter\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!filter->raw_op_with_indexing) {
|
||
- pr_err(
|
||
- "%s: Raw Transmit operation with indexing support must be configured before the Indexing operation\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!filter->pes_analysis_op_set) {
|
||
- pr_err(
|
||
- "%s: PES Analysis operation must precede Indexing operation\n",
|
||
- __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Bits [19:15] = 0, Bit[14] = Index by RAI,
|
||
- * Bits[13:12] = 0,
|
||
- * Bits[11:6] = Output pipe, Bit[5] = Source,
|
||
- * Bit[4] = Skip, Bits[3:0] = Opcode
|
||
- */
|
||
-
|
||
- reg |= TSPP2_OPCODE_INDEXING;
|
||
- if (op->params.indexing.skip_ts_errs)
|
||
- reg |= (0x1 << 4);
|
||
-
|
||
- if (op->params.indexing.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- reg |= ((pipe->hw_index & 0x3F) << 6);
|
||
-
|
||
- if (op->params.indexing.random_access_indicator_indexing)
|
||
- reg |= (0x1 << 14);
|
||
-
|
||
- /* Indexing table ID is set in the filter and not in the operation */
|
||
- filter->indexing_table_id = op->params.indexing.indexing_table_id;
|
||
- filter_reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
- filter_reg &= ~(0x3 << FILTER_ENTRY0_CODEC_OFFS);
|
||
- filter_reg |= (filter->indexing_table_id << FILTER_ENTRY0_CODEC_OFFS);
|
||
- writel_relaxed(filter_reg, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- filter->indexing_op_set = 1;
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_copy_op_write() - Write an Copy operation.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_copy_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- u32 reg = 0;
|
||
-
|
||
- /* Bits[19:6] = 0, Bit[5] = Source, Bit[4] = 0, Bits[3:0] = Opcode */
|
||
- reg |= TSPP2_OPCODE_COPY_PACKET;
|
||
- if (op->params.copy_packet.input == TSPP2_OP_BUFFER_B)
|
||
- reg |= (0x1 << 5);
|
||
-
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_OPCODE(filter->hw_index, op_index));
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_op_write() - Write an operation of any type.
|
||
- *
|
||
- * @filter: The filter to set the operation to.
|
||
- * @op: The operation.
|
||
- * @op_index: The operation's index in this filter.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_op_write(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *op,
|
||
- u8 op_index)
|
||
-{
|
||
- switch (op->type) {
|
||
- case TSPP2_OP_PES_ANALYSIS:
|
||
- return tspp2_pes_analysis_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_RAW_TRANSMIT:
|
||
- return tspp2_raw_tx_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_PES_TRANSMIT:
|
||
- return tspp2_pes_tx_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_PCR_EXTRACTION:
|
||
- return tspp2_pcr_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_CIPHER:
|
||
- return tspp2_cipher_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_INDEXING:
|
||
- return tspp2_index_op_write(filter, op, op_index);
|
||
- case TSPP2_OP_COPY_PACKET:
|
||
- return tspp2_copy_op_write(filter, op, op_index);
|
||
- default:
|
||
- pr_warn("%s: Unknown operation type\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_ops_add() - Set the operations of a disabled filter.
|
||
- *
|
||
- * @filter: The filter to work on.
|
||
- * @op: The new operations array.
|
||
- * @op_index: The number of operations in the array.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_filter_ops_add(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *ops,
|
||
- u8 operations_num)
|
||
-{
|
||
- int i;
|
||
- int ret = 0;
|
||
-
|
||
- /* User parameter validity checks were already performed */
|
||
-
|
||
- /*
|
||
- * We want to start with a clean slate here. The user may call us to
|
||
- * set operations several times, so need to make sure only the last call
|
||
- * counts.
|
||
- */
|
||
- tspp2_filter_ops_clear(filter);
|
||
-
|
||
- /* Save user operations in filter's database */
|
||
- for (i = 0; i < operations_num; i++)
|
||
- filter->operations[i] = ops[i];
|
||
-
|
||
- /* Write user operations to HW */
|
||
- for (i = 0; i < operations_num; i++) {
|
||
- ret = tspp2_op_write(filter, &ops[i], i);
|
||
- if (ret)
|
||
- goto ops_cleanup;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Here we want to add the Exit operation implicitly if required, that
|
||
- * is, if the user provided less than TSPP2_MAX_OPS_PER_FILTER
|
||
- * operations. However, we already called tspp2_filter_ops_clear()
|
||
- * which set all the operations in HW to Exit, before writing the
|
||
- * actual user operations. So, no need to do it again here.
|
||
- * Also, if someone calls this function with operations_num == 0,
|
||
- * it is similar to calling tspp2_filter_operations_clear().
|
||
- */
|
||
-
|
||
- filter->num_user_operations = operations_num;
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-ops_cleanup:
|
||
- pr_err("%s: Failed to set operations to filter, clearing all\n",
|
||
- __func__);
|
||
-
|
||
- tspp2_filter_ops_clear(filter);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_ops_update() - Update the operations of an enabled filter.
|
||
- *
|
||
- * This function updates the operations of an enabled filter. In fact, it is
|
||
- * not possible to update an existing filter without disabling it, clearing
|
||
- * the existing operations and setting new ones. However, if we do that,
|
||
- * we'll miss TS packets and not handle the stream properly, so a smooth
|
||
- * transition is required.
|
||
- * The algorithm is as follows:
|
||
- * 1. Find a free temporary filter object.
|
||
- * 2. Set the new filter's HW index to the reserved HW index.
|
||
- * 3. Set the operations to the new filter. This sets the operations to
|
||
- * the correct HW registers, based on the new HW index, and also updates
|
||
- * the relevant information in the temporary filter object. Later we copy this
|
||
- * to the actual filter object.
|
||
- * 4. Use the same context as the old filter (to maintain HW state).
|
||
- * 5. Reset parts of the context if needed.
|
||
- * 6. Enable the new HW filter, then disable the old filter.
|
||
- * 7. Update the source's reserved filter HW index.
|
||
- * 8. Update the filter's batch, HW index and operations-related information.
|
||
- *
|
||
- * @filter: The filter to work on.
|
||
- * @op: The new operations array.
|
||
- * @op_index: The number of operations in the array.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int tspp2_filter_ops_update(struct tspp2_filter *filter,
|
||
- const struct tspp2_operation *ops,
|
||
- u8 operations_num)
|
||
-{
|
||
- int i;
|
||
- int ret = 0;
|
||
- int found = 0;
|
||
- u32 reg = 0;
|
||
- u16 hw_idx;
|
||
- struct tspp2_filter_batch *batch;
|
||
- struct tspp2_filter *tmp_filter = NULL;
|
||
- struct tspp2_src *src = filter->src;
|
||
-
|
||
- /*
|
||
- * Find an available temporary filter object in the device's
|
||
- * filters database.
|
||
- */
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_FILTERS; i++)
|
||
- if (!src->device->filters[i].opened)
|
||
- break;
|
||
- if (i == TSPP2_NUM_AVAIL_FILTERS) {
|
||
- /* Should never happen */
|
||
- pr_err("%s: No available filters\n", __func__);
|
||
- return -ENOMEM;
|
||
- }
|
||
- tmp_filter = &src->device->filters[i];
|
||
-
|
||
- /*
|
||
- * Set new filter operations. We do this relatively early
|
||
- * in the function to avoid cleanup operations if this fails.
|
||
- * Since this also writes to HW, we have to set the correct HW index.
|
||
- */
|
||
- tmp_filter->hw_index = src->reserved_filter_hw_index;
|
||
- /*
|
||
- * Need to set the mask properly to indicate if the filter handles
|
||
- * a unique PID.
|
||
- */
|
||
- tmp_filter->mask = filter->mask;
|
||
- ret = tspp2_filter_ops_add(tmp_filter, ops, operations_num);
|
||
- if (ret) {
|
||
- tmp_filter->hw_index = 0;
|
||
- tmp_filter->mask = 0;
|
||
- return ret;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Mark new filter (in fact, the new filter HW index) as used in the
|
||
- * appropriate batch. The batch has to be one of the batches already
|
||
- * associated with the source.
|
||
- */
|
||
- list_for_each_entry(batch, &src->batches_list, link) {
|
||
- for (i = 0; i < TSPP2_FILTERS_PER_BATCH; i++) {
|
||
- hw_idx = (batch->batch_id *
|
||
- TSPP2_FILTERS_PER_BATCH) + i;
|
||
- if (hw_idx == tmp_filter->hw_index) {
|
||
- batch->hw_filters[i] = 1;
|
||
- found = 1;
|
||
- break;
|
||
- }
|
||
- }
|
||
- if (found)
|
||
- break;
|
||
- }
|
||
-
|
||
- if (!found) {
|
||
- pr_err("%s: Could not find matching batch\n", __func__);
|
||
- tspp2_filter_ops_clear(tmp_filter);
|
||
- tmp_filter->hw_index = 0;
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Set the same context of the old filter to the new HW filter */
|
||
- writel_relaxed((filter->context << FILTER_ENTRY1_CONTEXT_OFFS),
|
||
- filter->device->base +
|
||
- TSPP2_FILTER_ENTRY1(tmp_filter->hw_index));
|
||
-
|
||
- /*
|
||
- * Reset partial context, if necessary. We want to reset a partial
|
||
- * context before we start using it, so if there's a new operation
|
||
- * that uses a context where before there was no operation that used it,
|
||
- * we reset that context. We need to do this before we start using the
|
||
- * new operation, so before we enable the new filter.
|
||
- * Note: there is no need to reset most of the filter's context-based
|
||
- * counters, because the filter keeps using the same context. The
|
||
- * exception is the PES error counters that we may want to reset when
|
||
- * resetting the entire PES context.
|
||
- */
|
||
- if (!filter->pes_tx_op_set && tmp_filter->pes_tx_op_set) {
|
||
- /* PES Tx operation added */
|
||
- writel_relaxed(
|
||
- (0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_PES_CONTEXT_RESET(filter->context >> 5));
|
||
- writel_relaxed(0, filter->device->base +
|
||
- TSPP2_FILTER_PES_ERRORS(filter->context));
|
||
- }
|
||
-
|
||
- if (!filter->indexing_op_set && tmp_filter->indexing_op_set) {
|
||
- /* Indexing operation added */
|
||
- writel_relaxed(
|
||
- (0x1 << TSPP2_MODULUS_OP(filter->context, 32)),
|
||
- filter->device->base +
|
||
- TSPP2_INDEXING_CONTEXT_RESET(filter->context >> 5));
|
||
- }
|
||
-
|
||
- /*
|
||
- * Write PID and mask to new filter HW registers and enable it.
|
||
- * Preserve filter indexing table ID.
|
||
- */
|
||
- reg |= (0x1 << FILTER_ENTRY0_EN_OFFS);
|
||
- reg |= ((filter->pid_value << FILTER_ENTRY0_PID_OFFS) |
|
||
- (filter->mask << FILTER_ENTRY0_MASK_OFFS));
|
||
- reg |= (tmp_filter->indexing_table_id << FILTER_ENTRY0_CODEC_OFFS);
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(tmp_filter->hw_index));
|
||
-
|
||
- /* Disable old HW filter */
|
||
- writel_relaxed(0, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY0(filter->hw_index));
|
||
-
|
||
- /*
|
||
- * HW requires we wait for up to 2ms here before removing the
|
||
- * operations used by this filter.
|
||
- */
|
||
- udelay(TSPP2_HW_DELAY_USEC);
|
||
-
|
||
- tspp2_filter_ops_clear(filter);
|
||
-
|
||
- writel_relaxed(0, filter->device->base +
|
||
- TSPP2_FILTER_ENTRY1(filter->hw_index));
|
||
-
|
||
- /* Mark HW filter as unused in old batch */
|
||
- filter->batch->hw_filters[(filter->hw_index -
|
||
- (filter->batch->batch_id * TSPP2_FILTERS_PER_BATCH))] = 0;
|
||
-
|
||
- /* The new HW filter may be in a new batch, so we need to update */
|
||
- filter->batch = batch;
|
||
-
|
||
- /*
|
||
- * Update source's reserved filter HW index, and also update the
|
||
- * new HW index in the filter object.
|
||
- */
|
||
- src->reserved_filter_hw_index = filter->hw_index;
|
||
- filter->hw_index = tmp_filter->hw_index;
|
||
-
|
||
- /*
|
||
- * We've already set the new operations to HW, but we want to
|
||
- * update the filter object, too. tmp_filter contains all the
|
||
- * operations' related information we need (operations and flags).
|
||
- * Also, we make sure to update indexing_table_id based on the new
|
||
- * indexing operations.
|
||
- */
|
||
- memcpy(filter->operations, tmp_filter->operations,
|
||
- (sizeof(struct tspp2_operation) * TSPP2_MAX_OPS_PER_FILTER));
|
||
- filter->num_user_operations = tmp_filter->num_user_operations;
|
||
- filter->indexing_op_set = tmp_filter->indexing_op_set;
|
||
- filter->raw_op_with_indexing = tmp_filter->raw_op_with_indexing;
|
||
- filter->pes_analysis_op_set = tmp_filter->pes_analysis_op_set;
|
||
- filter->raw_op_set = tmp_filter->raw_op_set;
|
||
- filter->pes_tx_op_set = tmp_filter->pes_tx_op_set;
|
||
- filter->indexing_table_id = tmp_filter->indexing_table_id;
|
||
-
|
||
- /*
|
||
- * Now we can clean tmp_filter. This is really just to keep the filter
|
||
- * object clean. However, we don't want to use tspp2_filter_ops_clear()
|
||
- * because it clears the operations from HW too.
|
||
- */
|
||
- memset(tmp_filter->operations, 0,
|
||
- (sizeof(struct tspp2_operation) * TSPP2_MAX_OPS_PER_FILTER));
|
||
- tmp_filter->num_user_operations = 0;
|
||
- tmp_filter->indexing_op_set = 0;
|
||
- tmp_filter->raw_op_with_indexing = 0;
|
||
- tmp_filter->pes_analysis_op_set = 0;
|
||
- tmp_filter->raw_op_set = 0;
|
||
- tmp_filter->pes_tx_op_set = 0;
|
||
- tmp_filter->indexing_table_id = 0;
|
||
- tmp_filter->hw_index = 0;
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_filter_operations_set() - Set operations to a filter.
|
||
- *
|
||
- * @filter_handle: Filter to set operations to.
|
||
- * @ops: An array of up to TSPP2_MAX_OPS_PER_FILTER
|
||
- * operations.
|
||
- * @operations_num: Number of operations in the ops array.
|
||
- *
|
||
- * This function sets the required operations to a given filter. The filter
|
||
- * can either be disabled (in which case it may or may not already have some
|
||
- * operations set), or enabled (in which case it certainly has some oprations
|
||
- * set). In any case, the filter's previous operations are cleared, and the new
|
||
- * operations provided are set.
|
||
- *
|
||
- * In addition to some trivial parameter validity checks, the following
|
||
- * restrictions are enforced:
|
||
- * 1. A filter with a PES Analysis operation must handle a unique PID (i.e.,
|
||
- * should have a mask that equals TSPP2_UNIQUE_PID_MASK).
|
||
- * 2. Only a single Raw Transmit operation per filter can support HW indexing
|
||
- * (i.e., can have its support_indexing configuration parameter set).
|
||
- * 3. A PES Analysys operation must precede any PES Transmit operation.
|
||
- * 4. A PES Transmit operation with SW indexing (i.e., with its
|
||
- * enable_sw_indexing parameter set) must be preceded by a Raw Transmit
|
||
- * operation.
|
||
- * 5. Only a single indexing operation is supported per filter.
|
||
- * 6. A Raw Transmit operation with indexing support must be configured before
|
||
- * the Indexing operation.
|
||
- * 7. A PES Analysis operation must precede the Indexing operation.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_operations_set(u32 filter_handle,
|
||
- const struct tspp2_operation *ops,
|
||
- u8 operations_num)
|
||
-{
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- int ret = 0;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!ops || operations_num > TSPP2_MAX_OPS_PER_FILTER ||
|
||
- operations_num == 0) {
|
||
- pr_err("%s: Invalid ops parameter\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&filter->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (filter->enabled)
|
||
- ret = tspp2_filter_ops_update(filter, ops, operations_num);
|
||
- else
|
||
- ret = tspp2_filter_ops_add(filter, ops, operations_num);
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_operations_set);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_operations_clear() - Clear all operations from a filter.
|
||
- *
|
||
- * @filter_handle: Filter to clear all operations from.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_operations_clear(u32 filter_handle)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- mutex_lock(&filter->device->mutex);
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (filter->num_user_operations == 0) {
|
||
- pr_warn("%s: No operations to clear from filter\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return 0;
|
||
- }
|
||
-
|
||
- tspp2_filter_ops_clear(filter);
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_operations_clear);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_current_scrambling_bits_get() - Get the current scrambling bits.
|
||
- *
|
||
- * @filter_handle: Filter to get the scrambling bits from.
|
||
- * @scrambling_bits_value: The current value of the scrambling bits.
|
||
- * This could be the value from the TS packet
|
||
- * header, the value from the PES header, or a
|
||
- * logical OR operation of both values, depending
|
||
- * on the scrambling_bits_monitoring configuration
|
||
- * of the source this filter belongs to.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_current_scrambling_bits_get(u32 filter_handle,
|
||
- u8 *scrambling_bits_value)
|
||
-{
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- u32 reg;
|
||
- u32 ts_bits;
|
||
- u32 pes_bits;
|
||
- int ret;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (scrambling_bits_value == NULL) {
|
||
- pr_err("%s: Invalid parameter\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&filter->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_TSP_CONTEXT(filter->context));
|
||
-
|
||
- ts_bits = ((reg >> TSP_CONTEXT_TS_HEADER_SC_OFFS) & 0x3);
|
||
- pes_bits = ((reg >> TSP_CONTEXT_PES_HEADER_SC_OFFS) & 0x3);
|
||
-
|
||
- switch (filter->src->scrambling_bits_monitoring) {
|
||
- case TSPP2_SRC_SCRAMBLING_MONITOR_PES_ONLY:
|
||
- *scrambling_bits_value = pes_bits;
|
||
- break;
|
||
- case TSPP2_SRC_SCRAMBLING_MONITOR_TS_ONLY:
|
||
- *scrambling_bits_value = ts_bits;
|
||
- break;
|
||
- case TSPP2_SRC_SCRAMBLING_MONITOR_PES_AND_TS:
|
||
- *scrambling_bits_value = (pes_bits | ts_bits);
|
||
- break;
|
||
- case TSPP2_SRC_SCRAMBLING_MONITOR_NONE:
|
||
- /* fall through to default case */
|
||
- default:
|
||
- pr_err("%s: Invalid scrambling bits mode\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_current_scrambling_bits_get);
|
||
-
|
||
-/* Data-path API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_descriptor_get() - Get a data descriptor from a pipe.
|
||
- *
|
||
- * @pipe_handle: Pipe to get the descriptor from.
|
||
- * @desc: Received pipe data descriptor.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_pipe_descriptor_get(u32 pipe_handle, struct sps_iovec *desc)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
-
|
||
- if (!pipe) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!desc) {
|
||
- pr_err("%s: Invalid descriptor pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Descriptor pointer validity is checked inside the SPS driver. */
|
||
-
|
||
- ret = pm_runtime_get_sync(pipe->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&pipe->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!pipe->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe not opened\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = sps_get_iovec(pipe->sps_pipe, desc);
|
||
-
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return ret;
|
||
-
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_pipe_descriptor_get);
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_descriptor_put() - Release a descriptor for reuse by the pipe.
|
||
- *
|
||
- * @pipe_handle: Pipe to release the descriptor to.
|
||
- * @addr: Address to release for reuse.
|
||
- * @size: Size to release.
|
||
- * @flags: Descriptor flags.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_pipe_descriptor_put(u32 pipe_handle, u32 addr, u32 size, u32 flags)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
-
|
||
- if (!pipe) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(pipe->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&pipe->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!pipe->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe not opened\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = sps_transfer_one(pipe->sps_pipe, addr, size, NULL, flags);
|
||
-
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_pipe_descriptor_put);
|
||
-
|
||
-/**
|
||
- * tspp2_pipe_last_address_used_get() - Get the last address the TSPP2 used.
|
||
- *
|
||
- * @pipe_handle: Pipe to get the address from.
|
||
- * @address: The last (virtual) address TSPP2 wrote data to.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_pipe_last_address_used_get(u32 pipe_handle, u32 *address)
|
||
-{
|
||
- int ret;
|
||
- struct tspp2_pipe *pipe = (struct tspp2_pipe *)pipe_handle;
|
||
-
|
||
- if (!pipe) {
|
||
- pr_err("%s: Invalid pipe handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
- if (!address) {
|
||
- pr_err("%s: Invalid address pointer\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(pipe->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&pipe->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!pipe->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!pipe->opened) {
|
||
- pr_err("%s: Pipe not opened\n", __func__);
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- *address = readl_relaxed(pipe->device->base +
|
||
- TSPP2_PIPE_LAST_ADDRESS(pipe->hw_index));
|
||
-
|
||
- mutex_unlock(&pipe->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(pipe->device->dev);
|
||
- pm_runtime_put_autosuspend(pipe->device->dev);
|
||
-
|
||
- *address = be32_to_cpu(*address);
|
||
-
|
||
- dev_dbg(pipe->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_pipe_last_address_used_get);
|
||
-
|
||
-/**
|
||
- * tspp2_data_write() - Write (feed) data to a source.
|
||
- *
|
||
- * @src_handle: Source to feed data to.
|
||
- * @offset: Offset in the source's input pipe buffer.
|
||
- * @size: Size of data to write, in bytes.
|
||
- *
|
||
- * Schedule BAM transfers to feed data from the source's input pipe
|
||
- * to TSPP2 for processing. Note that the user is responsible for opening
|
||
- * an input pipe with the appropriate configuration parameters, and attaching
|
||
- * this pipe as an input pipe to the source. Pipe configuration validity is not
|
||
- * verified by this function.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_data_write(u32 src_handle, u32 offset, u32 size)
|
||
-{
|
||
- int ret;
|
||
- u32 desc_length;
|
||
- u32 desc_flags;
|
||
- u32 data_length = size;
|
||
- u32 data_offset = offset;
|
||
- struct tspp2_pipe *pipe;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (!src->enabled) {
|
||
- pr_err("%s: Source not enabled\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if ((src->input != TSPP2_INPUT_MEMORY) || !src->input_pipe) {
|
||
- pr_err("%s: Invalid source input or no input pipe\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- pipe = src->input_pipe;
|
||
-
|
||
- if (offset + size > pipe->cfg.buffer_size) {
|
||
- pr_err("%s: offset + size > buffer size\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- while (data_length) {
|
||
- if (data_length > pipe->cfg.sps_cfg.descriptor_size) {
|
||
- desc_length = pipe->cfg.sps_cfg.descriptor_size;
|
||
- desc_flags = 0;
|
||
- } else {
|
||
- /* last descriptor */
|
||
- desc_length = data_length;
|
||
- desc_flags = SPS_IOVEC_FLAG_EOT;
|
||
- }
|
||
-
|
||
- ret = sps_transfer_one(pipe->sps_pipe,
|
||
- pipe->iova + data_offset,
|
||
- desc_length,
|
||
- pipe->cfg.sps_cfg.user_info,
|
||
- desc_flags);
|
||
-
|
||
- if (ret) {
|
||
- pr_err("%s: sps_transfer_one failed, %d\n",
|
||
- __func__, ret);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return ret;
|
||
- }
|
||
-
|
||
- data_offset += desc_length;
|
||
- data_length -= desc_length;
|
||
- }
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_inval:
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_data_write);
|
||
-
|
||
-/**
|
||
- * tspp2_tsif_data_write() - Write (feed) data to a TSIF source via Loopback.
|
||
- *
|
||
- * @src_handle: Source to feed data to.
|
||
- * @data: data buffer containing one TS packet of size 188 Bytes.
|
||
- *
|
||
- * Write one TS packet of size 188 bytes to the TSIF loopback interface.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_tsif_data_write(u32 src_handle, u32 *data)
|
||
-{
|
||
- int i;
|
||
- int ret;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- struct tspp2_tsif_device *tsif_device;
|
||
- const unsigned int loopback_flags[3] = {0x01000000, 0, 0x02000000};
|
||
-
|
||
- if (data == NULL) {
|
||
- pr_err("%s: NULL data\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (!src->enabled) {
|
||
- pr_err("%s: Source not enabled\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if ((src->input != TSPP2_INPUT_TSIF0)
|
||
- && (src->input != TSPP2_INPUT_TSIF1)) {
|
||
- pr_err("%s: Invalid source input\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- tsif_device = &src->device->tsif_devices[src->input];
|
||
-
|
||
- /* lpbk_flags : start && !last */
|
||
- writel_relaxed(loopback_flags[0],
|
||
- tsif_device->base + TSPP2_TSIF_LPBK_FLAGS);
|
||
-
|
||
- /* 1-st dword of data */
|
||
- writel_relaxed(data[0],
|
||
- tsif_device->base + TSPP2_TSIF_LPBK_DATA);
|
||
-
|
||
- /* Clear start bit */
|
||
- writel_relaxed(loopback_flags[1],
|
||
- tsif_device->base + TSPP2_TSIF_LPBK_FLAGS);
|
||
-
|
||
- /* 45 more dwords */
|
||
- for (i = 1; i < 46; i++)
|
||
- writel_relaxed(data[i],
|
||
- tsif_device->base + TSPP2_TSIF_LPBK_DATA);
|
||
-
|
||
- /* Set last bit */
|
||
- writel_relaxed(loopback_flags[2],
|
||
- tsif_device->base + TSPP2_TSIF_LPBK_FLAGS);
|
||
-
|
||
- /* Last data dword */
|
||
- writel_relaxed(data[46], tsif_device->base + TSPP2_TSIF_LPBK_DATA);
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_inval:
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_tsif_data_write);
|
||
-
|
||
-/* Event notification API functions */
|
||
-
|
||
-/**
|
||
- * tspp2_global_event_notification_register() - Get notified on a global event.
|
||
- *
|
||
- * @dev_id: TSPP2 device ID.
|
||
- * @global_event_bitmask: A bitmask of global events,
|
||
- * TSPP2_GLOBAL_EVENT_XXX.
|
||
- * @callback: User callback function.
|
||
- * @cookie: User information passed to the callback.
|
||
- *
|
||
- * Register a user callback which will be invoked when certain global
|
||
- * events occur. Note the values (mask, callback and cookie) are overwritten
|
||
- * when calling this function multiple times. Therefore it is possible to
|
||
- * "unregister" a callback by calling this function with the bitmask set to 0
|
||
- * and with NULL callback and cookie.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_global_event_notification_register(u32 dev_id,
|
||
- u32 global_event_bitmask,
|
||
- void (*callback)(void *cookie, u32 event_bitmask),
|
||
- void *cookie)
|
||
-{
|
||
- struct tspp2_device *device;
|
||
- unsigned long flags;
|
||
- u32 reg = 0;
|
||
-
|
||
- if (dev_id >= TSPP2_NUM_DEVICES) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, dev_id);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- device = tspp2_devices[dev_id];
|
||
- if (!device) {
|
||
- pr_err("%s: Invalid device\n", __func__);
|
||
- return -ENODEV;
|
||
- }
|
||
-
|
||
- if (mutex_lock_interruptible(&device->mutex))
|
||
- return -ERESTARTSYS;
|
||
-
|
||
- if (!device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&device->mutex);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- /*
|
||
- * Some of the interrupts that are generated when these events occur
|
||
- * may be disabled due to module parameters. So we make sure to enable
|
||
- * them here, depending on which event was requested. If some events
|
||
- * were requested before and now this function is called again with
|
||
- * other events, though, we want to restore the interrupt configuration
|
||
- * to the default state according to the module parameters.
|
||
- */
|
||
- reg = readl_relaxed(device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
- if (global_event_bitmask & TSPP2_GLOBAL_EVENT_INVALID_AF_CTRL) {
|
||
- reg |= (0x1 << GLOBAL_IRQ_TSP_INVALID_AF_OFFS);
|
||
- } else {
|
||
- if (tspp2_en_invalid_af_ctrl)
|
||
- reg |= (0x1 << GLOBAL_IRQ_TSP_INVALID_AF_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << GLOBAL_IRQ_TSP_INVALID_AF_OFFS);
|
||
- }
|
||
-
|
||
- if (global_event_bitmask & TSPP2_GLOBAL_EVENT_INVALID_AF_LENGTH) {
|
||
- reg |= (0x1 << GLOBAL_IRQ_TSP_INVALID_LEN_OFFS);
|
||
- } else {
|
||
- if (tspp2_en_invalid_af_length)
|
||
- reg |= (0x1 << GLOBAL_IRQ_TSP_INVALID_LEN_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << GLOBAL_IRQ_TSP_INVALID_LEN_OFFS);
|
||
- }
|
||
-
|
||
- if (global_event_bitmask & TSPP2_GLOBAL_EVENT_PES_NO_SYNC) {
|
||
- reg |= (0x1 << GLOBAL_IRQ_PES_NO_SYNC_OFFS);
|
||
- } else {
|
||
- if (tspp2_en_pes_no_sync)
|
||
- reg |= (0x1 << GLOBAL_IRQ_PES_NO_SYNC_OFFS);
|
||
- else
|
||
- reg &= ~(0x1 << GLOBAL_IRQ_PES_NO_SYNC_OFFS);
|
||
- }
|
||
-
|
||
- writel_relaxed(reg, device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
-
|
||
- spin_lock_irqsave(&device->spinlock, flags);
|
||
- device->event_callback = callback;
|
||
- device->event_cookie = cookie;
|
||
- device->event_bitmask = global_event_bitmask;
|
||
- spin_unlock_irqrestore(&device->spinlock, flags);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- dev_dbg(device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_global_event_notification_register);
|
||
-
|
||
-/**
|
||
- * tspp2_src_event_notification_register() - Get notified on a source event.
|
||
- *
|
||
- * @src_handle: Source handle.
|
||
- * @src_event_bitmask: A bitmask of source events,
|
||
- * TSPP2_SRC_EVENT_XXX.
|
||
- * @callback: User callback function.
|
||
- * @cookie: User information passed to the callback.
|
||
- *
|
||
- * Register a user callback which will be invoked when certain source
|
||
- * events occur. Note the values (mask, callback and cookie) are overwritten
|
||
- * when calling this function multiple times. Therefore it is possible to
|
||
- * "unregister" a callback by calling this function with the bitmask set to 0
|
||
- * and with NULL callback and cookie.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_src_event_notification_register(u32 src_handle,
|
||
- u32 src_event_bitmask,
|
||
- void (*callback)(void *cookie, u32 event_bitmask),
|
||
- void *cookie)
|
||
-{
|
||
- int ret;
|
||
- u32 reg;
|
||
- unsigned long flags;
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
-
|
||
- if (!src) {
|
||
- pr_err("%s: Invalid source handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(src->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&src->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!src->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!src->opened) {
|
||
- pr_err("%s: Source not opened\n", __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if (((src->input == TSPP2_INPUT_TSIF0) ||
|
||
- (src->input == TSPP2_INPUT_TSIF1)) &&
|
||
- ((src_event_bitmask & TSPP2_SRC_EVENT_MEMORY_READ_ERROR) ||
|
||
- (src_event_bitmask & TSPP2_SRC_EVENT_FLOW_CTRL_STALL))) {
|
||
- pr_err("%s: Invalid event bitmask for a source with TSIF input\n",
|
||
- __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- if ((src->input == TSPP2_INPUT_MEMORY) &&
|
||
- ((src_event_bitmask & TSPP2_SRC_EVENT_TSIF_LOST_SYNC) ||
|
||
- (src_event_bitmask & TSPP2_SRC_EVENT_TSIF_TIMEOUT) ||
|
||
- (src_event_bitmask & TSPP2_SRC_EVENT_TSIF_OVERFLOW) ||
|
||
- (src_event_bitmask & TSPP2_SRC_EVENT_TSIF_PKT_READ_ERROR) ||
|
||
- (src_event_bitmask & TSPP2_SRC_EVENT_TSIF_PKT_WRITE_ERROR))) {
|
||
- pr_err("%s: Invalid event bitmask for a source with memory input\n",
|
||
- __func__);
|
||
- goto err_inval;
|
||
- }
|
||
-
|
||
- spin_lock_irqsave(&src->device->spinlock, flags);
|
||
- src->event_callback = callback;
|
||
- src->event_cookie = cookie;
|
||
- src->event_bitmask = src_event_bitmask;
|
||
- spin_unlock_irqrestore(&src->device->spinlock, flags);
|
||
-
|
||
- /* Enable/disable flow control stall interrupt on the source */
|
||
- reg = readl_relaxed(src->device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
- if (callback && (src_event_bitmask & TSPP2_SRC_EVENT_FLOW_CTRL_STALL)) {
|
||
- reg |= ((0x1 << src->hw_index) <<
|
||
- GLOBAL_IRQ_FC_STALL_OFFS);
|
||
- } else {
|
||
- reg &= ~((0x1 << src->hw_index) <<
|
||
- GLOBAL_IRQ_FC_STALL_OFFS);
|
||
- }
|
||
- writel_relaxed(reg, src->device->base + TSPP2_GLOBAL_IRQ_ENABLE);
|
||
-
|
||
- mutex_unlock(&src->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- dev_dbg(src->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_inval:
|
||
- mutex_unlock(&src->device->mutex);
|
||
- pm_runtime_mark_last_busy(src->device->dev);
|
||
- pm_runtime_put_autosuspend(src->device->dev);
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_src_event_notification_register);
|
||
-
|
||
-/**
|
||
- * tspp2_filter_event_notification_register() - Get notified on a filter event.
|
||
- *
|
||
- * @filter_handle: Filter handle.
|
||
- * @filter_event_bitmask: A bitmask of filter events,
|
||
- * TSPP2_FILTER_EVENT_XXX.
|
||
- * @callback: User callback function.
|
||
- * @cookie: User information passed to the callback.
|
||
- *
|
||
- * Register a user callback which will be invoked when certain filter
|
||
- * events occur. Note the values (mask, callback and cookie) are overwritten
|
||
- * when calling this function multiple times. Therefore it is possible to
|
||
- * "unregister" a callback by calling this function with the bitmask set to 0
|
||
- * and with NULL callback and cookie.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_filter_event_notification_register(u32 filter_handle,
|
||
- u32 filter_event_bitmask,
|
||
- void (*callback)(void *cookie, u32 event_bitmask),
|
||
- void *cookie)
|
||
-{
|
||
- int ret;
|
||
- int idx;
|
||
- u32 reg;
|
||
- unsigned long flags;
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
-
|
||
- if (!filter) {
|
||
- pr_err("%s: Invalid filter handle\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- ret = pm_runtime_get_sync(filter->device->dev);
|
||
- if (ret < 0)
|
||
- return ret;
|
||
-
|
||
- if (mutex_lock_interruptible(&filter->device->mutex)) {
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -ERESTARTSYS;
|
||
- }
|
||
-
|
||
- if (!filter->device->opened) {
|
||
- pr_err("%s: Device must be opened first\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EPERM;
|
||
- }
|
||
-
|
||
- if (!filter->opened) {
|
||
- pr_err("%s: Filter not opened\n", __func__);
|
||
- mutex_unlock(&filter->device->mutex);
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- spin_lock_irqsave(&filter->device->spinlock, flags);
|
||
- filter->event_callback = callback;
|
||
- filter->event_cookie = cookie;
|
||
- filter->event_bitmask = filter_event_bitmask;
|
||
- spin_unlock_irqrestore(&filter->device->spinlock, flags);
|
||
-
|
||
- /* Enable/disable SC high/low interrupts per filter as requested */
|
||
- idx = (filter->context >> 5);
|
||
- reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_SC_GO_HIGH_ENABLE(idx));
|
||
- if (callback &&
|
||
- (filter_event_bitmask & TSPP2_FILTER_EVENT_SCRAMBLING_HIGH)) {
|
||
- reg |= (0x1 << TSPP2_MODULUS_OP(filter->context, 32));
|
||
- } else {
|
||
- reg &= ~(0x1 << TSPP2_MODULUS_OP(filter->context, 32));
|
||
- }
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_SC_GO_HIGH_ENABLE(idx));
|
||
-
|
||
- reg = readl_relaxed(filter->device->base +
|
||
- TSPP2_SC_GO_LOW_ENABLE(idx));
|
||
- if (callback &&
|
||
- (filter_event_bitmask & TSPP2_FILTER_EVENT_SCRAMBLING_LOW)) {
|
||
- reg |= (0x1 << TSPP2_MODULUS_OP(filter->context, 32));
|
||
- } else {
|
||
- reg &= ~(0x1 << TSPP2_MODULUS_OP(filter->context, 32));
|
||
- }
|
||
- writel_relaxed(reg, filter->device->base +
|
||
- TSPP2_SC_GO_LOW_ENABLE(idx));
|
||
-
|
||
- mutex_unlock(&filter->device->mutex);
|
||
-
|
||
- pm_runtime_mark_last_busy(filter->device->dev);
|
||
- pm_runtime_put_autosuspend(filter->device->dev);
|
||
-
|
||
- dev_dbg(filter->device->dev, "%s: successful\n", __func__);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_filter_event_notification_register);
|
||
-
|
||
-/**
|
||
- * tspp2_get_filter_hw_index() - Get a filter's hardware index.
|
||
- *
|
||
- * @filter_handle: Filter handle.
|
||
- *
|
||
- * This is an helper function to support tspp2 auto-testing.
|
||
- *
|
||
- * Return the filter's hardware index on success, error value otherwise.
|
||
- */
|
||
-int tspp2_get_filter_hw_index(u32 filter_handle)
|
||
-{
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- if (!filter_handle)
|
||
- return -EINVAL;
|
||
- return filter->hw_index;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_get_filter_hw_index);
|
||
-
|
||
-/**
|
||
- * tspp2_get_reserved_hw_index() - Get a source's reserved hardware index.
|
||
- *
|
||
- * @src_handle: Source handle.
|
||
- *
|
||
- * This is an helper function to support tspp2 auto-testing.
|
||
- *
|
||
- * Return the source's reserved hardware index on success,
|
||
- * error value otherwise.
|
||
- */
|
||
-int tspp2_get_reserved_hw_index(u32 src_handle)
|
||
-{
|
||
- struct tspp2_src *src = (struct tspp2_src *)src_handle;
|
||
- if (!src_handle)
|
||
- return -EINVAL;
|
||
- return src->reserved_filter_hw_index;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_get_reserved_hw_index);
|
||
-
|
||
-/**
|
||
- * tspp2_get_ops_array() - Get filter's operations.
|
||
- *
|
||
- * @filter_handle: Filter handle.
|
||
- * @ops_array: The filter's operations.
|
||
- * @num_of_ops: The filter's number of operations.
|
||
- *
|
||
- * This is an helper function to support tspp2 auto-testing.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-int tspp2_get_ops_array(u32 filter_handle,
|
||
- struct tspp2_operation ops_array[TSPP2_MAX_OPS_PER_FILTER],
|
||
- u8 *num_of_ops)
|
||
-{
|
||
- int i;
|
||
- struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
|
||
- if (!filter_handle || !num_of_ops)
|
||
- return -EINVAL;
|
||
- *num_of_ops = filter->num_user_operations;
|
||
- for (i = 0; i < *num_of_ops; i++)
|
||
- ops_array[i] = filter->operations[i];
|
||
- return 0;
|
||
-}
|
||
-EXPORT_SYMBOL(tspp2_get_ops_array);
|
||
-
|
||
-/* Platform driver related functions: */
|
||
-
|
||
-/**
|
||
- * msm_tspp2_dt_to_pdata() - Copy device-tree data to platfrom data structure.
|
||
- *
|
||
- * @pdev: Platform device.
|
||
- *
|
||
- * Return pointer to allocated platform data on success, NULL on failure.
|
||
- */
|
||
-static struct msm_tspp2_platform_data *
|
||
-msm_tspp2_dt_to_pdata(struct platform_device *pdev)
|
||
-{
|
||
- struct device_node *node = pdev->dev.of_node;
|
||
- struct msm_tspp2_platform_data *data;
|
||
- int rc;
|
||
-
|
||
- /* Note: memory allocated by devm_kzalloc is freed automatically */
|
||
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||
- if (!data) {
|
||
- pr_err("%s: Unable to allocate platform data\n", __func__);
|
||
- return NULL;
|
||
- }
|
||
-
|
||
- /* Get power regulator */
|
||
- if (!of_get_property(node, "vdd-supply", NULL)) {
|
||
- pr_err("%s: Could not find vdd-supply property\n", __func__);
|
||
- return NULL;
|
||
- }
|
||
-
|
||
- /* Get IOMMU information */
|
||
- rc = of_property_read_string(node, "qcom,iommu-hlos-group",
|
||
- &data->hlos_group);
|
||
- if (rc) {
|
||
- pr_err("%s: Could not find iommu-hlos-group property, err = %d\n",
|
||
- __func__, rc);
|
||
- return NULL;
|
||
- }
|
||
- rc = of_property_read_string(node, "qcom,iommu-cpz-group",
|
||
- &data->cpz_group);
|
||
- if (rc) {
|
||
- pr_err("%s: Could not find iommu-cpz-group property, err = %d\n",
|
||
- __func__, rc);
|
||
- return NULL;
|
||
- }
|
||
- rc = of_property_read_u32(node, "qcom,iommu-hlos-partition",
|
||
- &data->hlos_partition);
|
||
- if (rc) {
|
||
- pr_err("%s: Could not find iommu-hlos-partition property, err = %d\n",
|
||
- __func__, rc);
|
||
- return NULL;
|
||
- }
|
||
- rc = of_property_read_u32(node, "qcom,iommu-cpz-partition",
|
||
- &data->cpz_partition);
|
||
- if (rc) {
|
||
- pr_err("%s: Could not find iommu-cpz-partition property, err = %d\n",
|
||
- __func__, rc);
|
||
- return NULL;
|
||
- }
|
||
-
|
||
- return data;
|
||
-}
|
||
-
|
||
-static void msm_tspp2_iommu_info_free(struct tspp2_device *device)
|
||
-{
|
||
- if (device->iommu_info.hlos_group) {
|
||
- iommu_group_put(device->iommu_info.hlos_group);
|
||
- device->iommu_info.hlos_group = NULL;
|
||
- }
|
||
-
|
||
- if (device->iommu_info.cpz_group) {
|
||
- iommu_group_put(device->iommu_info.cpz_group);
|
||
- device->iommu_info.cpz_group = NULL;
|
||
- }
|
||
-
|
||
- device->iommu_info.hlos_domain = NULL;
|
||
- device->iommu_info.cpz_domain = NULL;
|
||
- device->iommu_info.hlos_domain_num = -1;
|
||
- device->iommu_info.cpz_domain_num = -1;
|
||
- device->iommu_info.hlos_partition = -1;
|
||
- device->iommu_info.cpz_partition = -1;
|
||
-}
|
||
-
|
||
-/**
|
||
- * msm_tspp2_iommu_info_get() - Get IOMMU information.
|
||
- *
|
||
- * @pdev: Platform device, containing platform information.
|
||
- * @device: TSPP2 device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int msm_tspp2_iommu_info_get(struct platform_device *pdev,
|
||
- struct tspp2_device *device)
|
||
-{
|
||
- int ret = 0;
|
||
- struct msm_tspp2_platform_data *data = pdev->dev.platform_data;
|
||
-
|
||
- device->iommu_info.hlos_group = NULL;
|
||
- device->iommu_info.cpz_group = NULL;
|
||
- device->iommu_info.hlos_domain = NULL;
|
||
- device->iommu_info.cpz_domain = NULL;
|
||
- device->iommu_info.hlos_domain_num = -1;
|
||
- device->iommu_info.cpz_domain_num = -1;
|
||
- device->iommu_info.hlos_partition = -1;
|
||
- device->iommu_info.cpz_partition = -1;
|
||
-
|
||
- device->iommu_info.hlos_group = iommu_group_find(data->hlos_group);
|
||
- if (!device->iommu_info.hlos_group) {
|
||
- dev_err(&pdev->dev, "%s: Cannot find IOMMU HLOS group",
|
||
- __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_out;
|
||
- }
|
||
- device->iommu_info.cpz_group = iommu_group_find(data->cpz_group);
|
||
- if (!device->iommu_info.cpz_group) {
|
||
- dev_err(&pdev->dev, "%s: Cannot find IOMMU CPZ group",
|
||
- __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_out;
|
||
- }
|
||
-
|
||
- device->iommu_info.hlos_domain =
|
||
- iommu_group_get_iommudata(device->iommu_info.hlos_group);
|
||
- if (IS_ERR_OR_NULL(device->iommu_info.hlos_domain)) {
|
||
- dev_err(&pdev->dev, "%s: iommu_group_get_iommudata failed",
|
||
- __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_out;
|
||
- }
|
||
-
|
||
- device->iommu_info.cpz_domain =
|
||
- iommu_group_get_iommudata(device->iommu_info.cpz_group);
|
||
- if (IS_ERR_OR_NULL(device->iommu_info.cpz_domain)) {
|
||
- device->iommu_info.hlos_domain = NULL;
|
||
- dev_err(&pdev->dev, "%s: iommu_group_get_iommudata failed",
|
||
- __func__);
|
||
- ret = -EINVAL;
|
||
- goto err_out;
|
||
- }
|
||
-
|
||
- device->iommu_info.hlos_domain_num =
|
||
- msm_find_domain_no(device->iommu_info.hlos_domain);
|
||
- device->iommu_info.cpz_domain_num =
|
||
- msm_find_domain_no(device->iommu_info.cpz_domain);
|
||
- device->iommu_info.hlos_partition = data->hlos_partition;
|
||
- device->iommu_info.cpz_partition = data->cpz_partition;
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_out:
|
||
- msm_tspp2_iommu_info_free(device);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_clocks_put() - Put clocks and disable regulator.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- */
|
||
-static void tspp2_clocks_put(struct tspp2_device *device)
|
||
-{
|
||
- if (device->tsif_ref_clk)
|
||
- clk_put(device->tsif_ref_clk);
|
||
-
|
||
- if (device->tspp2_klm_ahb_clk)
|
||
- clk_put(device->tspp2_klm_ahb_clk);
|
||
-
|
||
- if (device->tspp2_vbif_clk)
|
||
- clk_put(device->tspp2_vbif_clk);
|
||
-
|
||
- if (device->vbif_ahb_clk)
|
||
- clk_put(device->vbif_ahb_clk);
|
||
-
|
||
- if (device->vbif_axi_clk)
|
||
- clk_put(device->vbif_axi_clk);
|
||
-
|
||
- if (device->tspp2_core_clk)
|
||
- clk_put(device->tspp2_core_clk);
|
||
-
|
||
- if (device->tspp2_ahb_clk)
|
||
- clk_put(device->tspp2_ahb_clk);
|
||
-
|
||
- device->tspp2_ahb_clk = NULL;
|
||
- device->tspp2_core_clk = NULL;
|
||
- device->tspp2_vbif_clk = NULL;
|
||
- device->vbif_ahb_clk = NULL;
|
||
- device->vbif_axi_clk = NULL;
|
||
- device->tspp2_klm_ahb_clk = NULL;
|
||
- device->tsif_ref_clk = NULL;
|
||
-}
|
||
-
|
||
-/**
|
||
- * msm_tspp2_clocks_setup() - Get clocks and set their rate, enable regulator.
|
||
- *
|
||
- * @pdev: Platform device, containing platform information.
|
||
- * @device: TSPP2 device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int msm_tspp2_clocks_setup(struct platform_device *pdev,
|
||
- struct tspp2_device *device)
|
||
-{
|
||
- int ret = 0;
|
||
- unsigned long rate_in_hz = 0;
|
||
- struct clk *tspp2_core_clk_src = NULL;
|
||
-
|
||
- /* Get power regulator (GDSC) */
|
||
- device->gdsc = devm_regulator_get(&pdev->dev, "vdd");
|
||
- if (IS_ERR(device->gdsc)) {
|
||
- pr_err("%s: Failed to get vdd power regulator\n", __func__);
|
||
- ret = PTR_ERR(device->gdsc);
|
||
- device->gdsc = NULL;
|
||
- return ret;
|
||
- }
|
||
-
|
||
- device->tspp2_ahb_clk = NULL;
|
||
- device->tspp2_core_clk = NULL;
|
||
- device->tspp2_vbif_clk = NULL;
|
||
- device->vbif_ahb_clk = NULL;
|
||
- device->vbif_axi_clk = NULL;
|
||
- device->tspp2_klm_ahb_clk = NULL;
|
||
- device->tsif_ref_clk = NULL;
|
||
-
|
||
- device->tspp2_ahb_clk = clk_get(&pdev->dev, "bcc_tspp2_ahb_clk");
|
||
- if (IS_ERR(device->tspp2_ahb_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "bcc_tspp2_ahb_clk");
|
||
- ret = PTR_ERR(device->tspp2_ahb_clk);
|
||
- device->tspp2_ahb_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->tspp2_core_clk = clk_get(&pdev->dev, "bcc_tspp2_core_clk");
|
||
- if (IS_ERR(device->tspp2_core_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "bcc_tspp2_core_clk");
|
||
- ret = PTR_ERR(device->tspp2_core_clk);
|
||
- device->tspp2_core_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->tspp2_vbif_clk = clk_get(&pdev->dev, "bcc_vbif_tspp2_clk");
|
||
- if (IS_ERR(device->tspp2_vbif_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "bcc_vbif_tspp2_clk");
|
||
- ret = PTR_ERR(device->tspp2_vbif_clk);
|
||
- device->tspp2_vbif_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->vbif_ahb_clk = clk_get(&pdev->dev, "iface_vbif_clk");
|
||
- if (IS_ERR(device->vbif_ahb_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "iface_vbif_clk");
|
||
- ret = PTR_ERR(device->vbif_ahb_clk);
|
||
- device->vbif_ahb_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->vbif_axi_clk = clk_get(&pdev->dev, "vbif_core_clk");
|
||
- if (IS_ERR(device->vbif_axi_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "vbif_core_clk");
|
||
- ret = PTR_ERR(device->vbif_axi_clk);
|
||
- device->vbif_axi_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->tspp2_klm_ahb_clk = clk_get(&pdev->dev, "bcc_klm_ahb_clk");
|
||
- if (IS_ERR(device->tspp2_klm_ahb_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "bcc_klm_ahb_clk");
|
||
- ret = PTR_ERR(device->tspp2_klm_ahb_clk);
|
||
- device->tspp2_klm_ahb_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- device->tsif_ref_clk = clk_get(&pdev->dev, "gcc_tsif_ref_clk");
|
||
- if (IS_ERR(device->tsif_ref_clk)) {
|
||
- pr_err("%s: Failed to get %s", __func__, "gcc_tsif_ref_clk");
|
||
- ret = PTR_ERR(device->tsif_ref_clk);
|
||
- device->tsif_ref_clk = NULL;
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- /* Set relevant clock rates */
|
||
- rate_in_hz = clk_round_rate(device->tsif_ref_clk, 1);
|
||
- if (clk_set_rate(device->tsif_ref_clk, rate_in_hz)) {
|
||
- pr_err("%s: Failed to set rate %lu to %s\n", __func__,
|
||
- rate_in_hz, "gcc_tsif_ref_clk");
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- /* We need to set the rate of tspp2_core_clk_src */
|
||
- tspp2_core_clk_src = clk_get_parent(device->tspp2_core_clk);
|
||
- if (tspp2_core_clk_src) {
|
||
- rate_in_hz = clk_round_rate(tspp2_core_clk_src, 1);
|
||
- if (clk_set_rate(tspp2_core_clk_src, rate_in_hz)) {
|
||
- pr_err("%s: Failed to set rate %lu to tspp2_core_clk_src\n",
|
||
- __func__, rate_in_hz);
|
||
- goto err_clocks;
|
||
- }
|
||
- } else {
|
||
- pr_err("%s: Failed to get tspp2_core_clk parent\n", __func__);
|
||
- goto err_clocks;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_clocks:
|
||
- tspp2_clocks_put(device);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-/**
|
||
- * msm_tspp2_map_io_memory() - Map memory resources to kernel space.
|
||
- *
|
||
- * @pdev: Platform device, containing platform information.
|
||
- * @device: TSPP2 device.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int msm_tspp2_map_io_memory(struct platform_device *pdev,
|
||
- struct tspp2_device *device)
|
||
-{
|
||
- struct resource *mem_tsif0;
|
||
- struct resource *mem_tsif1;
|
||
- struct resource *mem_tspp2;
|
||
- struct resource *mem_bam;
|
||
-
|
||
- /* Get memory resources */
|
||
- mem_tsif0 = platform_get_resource_byname(pdev,
|
||
- IORESOURCE_MEM, "MSM_TSIF0");
|
||
- if (!mem_tsif0) {
|
||
- dev_err(&pdev->dev, "%s: Missing TSIF0 MEM resource", __func__);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- mem_tsif1 = platform_get_resource_byname(pdev,
|
||
- IORESOURCE_MEM, "MSM_TSIF1");
|
||
- if (!mem_tsif1) {
|
||
- dev_err(&pdev->dev, "%s: Missing TSIF1 MEM resource", __func__);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- mem_tspp2 = platform_get_resource_byname(pdev,
|
||
- IORESOURCE_MEM, "MSM_TSPP2");
|
||
- if (!mem_tspp2) {
|
||
- dev_err(&pdev->dev, "%s: Missing TSPP2 MEM resource", __func__);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- mem_bam = platform_get_resource_byname(pdev,
|
||
- IORESOURCE_MEM, "MSM_TSPP2_BAM");
|
||
- if (!mem_bam) {
|
||
- dev_err(&pdev->dev, "%s: Missing BAM MEM resource", __func__);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- /* Map memory physical addresses to kernel space */
|
||
- device->tsif_devices[0].base = ioremap(mem_tsif0->start,
|
||
- resource_size(mem_tsif0));
|
||
- if (!device->tsif_devices[0].base) {
|
||
- dev_err(&pdev->dev, "%s: ioremap failed", __func__);
|
||
- goto err_map_tsif0;
|
||
- }
|
||
-
|
||
- device->tsif_devices[1].base = ioremap(mem_tsif1->start,
|
||
- resource_size(mem_tsif1));
|
||
- if (!device->tsif_devices[1].base) {
|
||
- dev_err(&pdev->dev, "%s: ioremap failed", __func__);
|
||
- goto err_map_tsif1;
|
||
- }
|
||
-
|
||
- device->base = ioremap(mem_tspp2->start, resource_size(mem_tspp2));
|
||
- if (!device->base) {
|
||
- dev_err(&pdev->dev, "%s: ioremap failed", __func__);
|
||
- goto err_map_dev;
|
||
- }
|
||
-
|
||
- memset(&device->bam_props, 0, sizeof(device->bam_props));
|
||
- device->bam_props.phys_addr = mem_bam->start;
|
||
- device->bam_props.virt_addr = ioremap(mem_bam->start,
|
||
- resource_size(mem_bam));
|
||
- if (!device->bam_props.virt_addr) {
|
||
- dev_err(&pdev->dev, "%s: ioremap failed", __func__);
|
||
- goto err_map_bam;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-
|
||
-err_map_bam:
|
||
- iounmap(device->base);
|
||
-
|
||
-err_map_dev:
|
||
- iounmap(device->tsif_devices[1].base);
|
||
-
|
||
-err_map_tsif1:
|
||
- iounmap(device->tsif_devices[0].base);
|
||
-
|
||
-err_map_tsif0:
|
||
- return -ENXIO;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_event_work_prepare() - Prepare and queue a work element.
|
||
- *
|
||
- * @device: TSPP2 device.
|
||
- * @callback: User callback to invoke.
|
||
- * @cookie: User cookie.
|
||
- * @event_bitmask: Event bitmask
|
||
- *
|
||
- * Get a free work element from the pool, prepare it and queue it
|
||
- * to the work queue. When scheduled, the work will invoke the user callback
|
||
- * for the event that the HW reported.
|
||
- */
|
||
-static void tspp2_event_work_prepare(struct tspp2_device *device,
|
||
- void (*callback)(void *cookie, u32 event_bitmask),
|
||
- void *cookie,
|
||
- u32 event_bitmask)
|
||
-{
|
||
- struct tspp2_event_work *work = NULL;
|
||
-
|
||
- if (!list_empty(&device->free_work_list)) {
|
||
- work = list_first_entry(&device->free_work_list,
|
||
- struct tspp2_event_work, link);
|
||
- list_del(&work->link);
|
||
- work->callback = callback;
|
||
- work->cookie = cookie;
|
||
- work->event_bitmask = event_bitmask;
|
||
- queue_work(device->work_queue, &work->work);
|
||
- } else {
|
||
- pr_warn("%s: No available work element\n", __func__);
|
||
- }
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_isr() - TSPP2 interrupt handler.
|
||
- *
|
||
- * @irq: Interrupt number.
|
||
- * @dev: TSPP2 device.
|
||
- *
|
||
- * Handle TSPP2 HW interrupt. Collect relevant statistics and invoke
|
||
- * user registered callbacks for global, source or filter events.
|
||
- *
|
||
- * Return IRQ_HANDLED.
|
||
- */
|
||
-static irqreturn_t tspp2_isr(int irq, void *dev)
|
||
-{
|
||
- struct tspp2_device *device = dev;
|
||
- struct tspp2_src *src = NULL;
|
||
- struct tspp2_filter *f = NULL;
|
||
- unsigned long ext_reg = 0;
|
||
- unsigned long val = 0;
|
||
- unsigned long flags;
|
||
- u32 i = 0, j = 0;
|
||
- u32 global_bitmask = 0;
|
||
- u32 src_bitmask[TSPP2_NUM_MEM_INPUTS] = {0};
|
||
- u32 filter_bitmask[TSPP2_NUM_CONTEXTS] = {0};
|
||
- u32 reg = 0;
|
||
-
|
||
- reg = readl_relaxed(device->base + TSPP2_GLOBAL_IRQ_STATUS);
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_TSP_INVALID_AF_OFFS)) {
|
||
- device->irq_stats.global.tsp_invalid_af_control++;
|
||
- global_bitmask |= TSPP2_GLOBAL_EVENT_INVALID_AF_CTRL;
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_TSP_INVALID_LEN_OFFS)) {
|
||
- device->irq_stats.global.tsp_invalid_length++;
|
||
- global_bitmask |= TSPP2_GLOBAL_EVENT_INVALID_AF_LENGTH;
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_PES_NO_SYNC_OFFS)) {
|
||
- device->irq_stats.global.pes_no_sync++;
|
||
- global_bitmask |= TSPP2_GLOBAL_EVENT_PES_NO_SYNC;
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_ENCRYPT_LEVEL_ERR_OFFS))
|
||
- device->irq_stats.global.encrypt_level_err++;
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_KEY_NOT_READY_OFFS)) {
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_KEY_NOT_READY_IRQ_STATUS);
|
||
- for_each_set_bit(i, &ext_reg, TSPP2_NUM_KEYTABLES)
|
||
- device->irq_stats.kt[i].key_not_ready++;
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_KEY_NOT_READY_IRQ_CLEAR);
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_UNEXPECTED_RESET_OFFS)) {
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_UNEXPECTED_RST_IRQ_STATUS);
|
||
- for_each_set_bit(i, &ext_reg, TSPP2_NUM_PIPES)
|
||
- device->irq_stats.pipe[i].unexpected_reset++;
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_UNEXPECTED_RST_IRQ_CLEAR);
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_WRONG_PIPE_DIR_OFFS)) {
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_WRONG_PIPE_DIR_IRQ_STATUS);
|
||
- for_each_set_bit(i, &ext_reg, TSPP2_NUM_PIPES)
|
||
- device->irq_stats.pipe[i].wrong_pipe_direction++;
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_WRONG_PIPE_DIR_IRQ_CLEAR);
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_QSB_RESP_ERR_OFFS)) {
|
||
- global_bitmask |= TSPP2_GLOBAL_EVENT_TX_FAIL;
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_QSB_RESPONSE_ERROR_IRQ_STATUS);
|
||
- for_each_set_bit(i, &ext_reg, TSPP2_NUM_PIPES)
|
||
- device->irq_stats.pipe[i].qsb_response_error++;
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_QSB_RESPONSE_ERROR_IRQ_CLEAR);
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_SC_GO_HIGH_OFFS)) {
|
||
- for (j = 0; j < 3; j++) {
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_SC_GO_HIGH_STATUS(j));
|
||
- for_each_set_bit(i, &ext_reg, 32) {
|
||
- filter_bitmask[j*32 + i] |=
|
||
- TSPP2_FILTER_EVENT_SCRAMBLING_HIGH;
|
||
- device->irq_stats.ctx[j*32 + i].sc_go_high++;
|
||
- }
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_SC_GO_HIGH_CLEAR(j));
|
||
- }
|
||
- }
|
||
-
|
||
- if (reg & (0x1 << GLOBAL_IRQ_SC_GO_LOW_OFFS)) {
|
||
- for (j = 0; j < 3; j++) {
|
||
- ext_reg = readl_relaxed(device->base +
|
||
- TSPP2_SC_GO_LOW_STATUS(j));
|
||
- for_each_set_bit(i, &ext_reg, 32) {
|
||
- filter_bitmask[j*32 + i] |=
|
||
- TSPP2_FILTER_EVENT_SCRAMBLING_LOW;
|
||
- device->irq_stats.ctx[j*32 + i].sc_go_low++;
|
||
- }
|
||
- writel_relaxed(ext_reg, device->base +
|
||
- TSPP2_SC_GO_LOW_CLEAR(j));
|
||
- }
|
||
- }
|
||
-
|
||
- if (reg & (0xFF << GLOBAL_IRQ_READ_FAIL_OFFS)) {
|
||
- val = ((reg & (0xFF << GLOBAL_IRQ_READ_FAIL_OFFS)) >>
|
||
- GLOBAL_IRQ_READ_FAIL_OFFS);
|
||
- for_each_set_bit(i, &val, TSPP2_NUM_MEM_INPUTS) {
|
||
- src_bitmask[i] |= TSPP2_SRC_EVENT_MEMORY_READ_ERROR;
|
||
- device->irq_stats.src[i].read_failure++;
|
||
- }
|
||
- }
|
||
-
|
||
- if (reg & (0xFF << GLOBAL_IRQ_FC_STALL_OFFS)) {
|
||
- val = ((reg & (0xFF << GLOBAL_IRQ_FC_STALL_OFFS)) >>
|
||
- GLOBAL_IRQ_FC_STALL_OFFS);
|
||
- for_each_set_bit(i, &val, TSPP2_NUM_MEM_INPUTS) {
|
||
- src_bitmask[i] |= TSPP2_SRC_EVENT_FLOW_CTRL_STALL;
|
||
- device->irq_stats.src[i].flow_control_stall++;
|
||
- }
|
||
- }
|
||
-
|
||
- spin_lock_irqsave(&device->spinlock, flags);
|
||
-
|
||
- /* Invoke user callback for global events */
|
||
- if (device->event_callback && (global_bitmask & device->event_bitmask))
|
||
- tspp2_event_work_prepare(device, device->event_callback,
|
||
- device->event_cookie,
|
||
- (global_bitmask & device->event_bitmask));
|
||
-
|
||
- /* Invoke user callbacks on memory source events */
|
||
- for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
|
||
- src = &device->mem_sources[i];
|
||
- if (src->event_callback &&
|
||
- (src_bitmask[src->hw_index] & src->event_bitmask))
|
||
- tspp2_event_work_prepare(device,
|
||
- src->event_callback,
|
||
- src->event_cookie,
|
||
- (src_bitmask[src->hw_index] &
|
||
- src->event_bitmask));
|
||
- }
|
||
-
|
||
- /* Invoke user callbacks on filter events */
|
||
- for (i = 0; i < TSPP2_NUM_AVAIL_FILTERS; i++) {
|
||
- f = &device->filters[i];
|
||
- if (f->event_callback &&
|
||
- (f->event_bitmask & filter_bitmask[f->context]))
|
||
- tspp2_event_work_prepare(device,
|
||
- f->event_callback,
|
||
- f->event_cookie,
|
||
- (f->event_bitmask &
|
||
- filter_bitmask[f->context]));
|
||
- }
|
||
-
|
||
- spin_unlock_irqrestore(&device->spinlock, flags);
|
||
-
|
||
- /*
|
||
- * Clear global interrupts. Note bits [9:4] are an aggregation of
|
||
- * other IRQs, and are reserved in the TSPP2_GLOBAL_IRQ_CLEAR register.
|
||
- */
|
||
- reg &= ~(0x0FFF << GLOBAL_IRQ_CLEAR_RESERVED_OFFS);
|
||
- writel_relaxed(reg, device->base + TSPP2_GLOBAL_IRQ_CLEAR);
|
||
- /*
|
||
- * Before returning IRQ_HANDLED to the generic interrupt handling
|
||
- * framework, we need to make sure all operations, including clearing of
|
||
- * interrupt status registers in the hardware, are performed.
|
||
- * Thus a barrier after clearing the interrupt status register
|
||
- * is required to guarantee that the interrupt status register has
|
||
- * really been cleared by the time we return from this handler.
|
||
- */
|
||
- wmb();
|
||
-
|
||
- return IRQ_HANDLED;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tsif_isr() - TSIF interrupt handler.
|
||
- *
|
||
- * @irq: Interrupt number.
|
||
- * @dev: TSIF device that generated the interrupt.
|
||
- *
|
||
- * Handle TSIF HW interrupt. Collect HW statistics and, if the user registered
|
||
- * a relevant source callback, invoke it.
|
||
- *
|
||
- * Return IRQ_HANDLED on success, IRQ_NONE on irrelevant interrupts.
|
||
- */
|
||
-static irqreturn_t tsif_isr(int irq, void *dev)
|
||
-{
|
||
- u32 src_bitmask = 0;
|
||
- unsigned long flags;
|
||
- struct tspp2_src *src = NULL;
|
||
- struct tspp2_tsif_device *tsif_device = dev;
|
||
- u32 sts_ctl = 0;
|
||
-
|
||
- sts_ctl = readl_relaxed(tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
-
|
||
- if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
|
||
- TSIF_STS_CTL_PKT_WRITE_ERR |
|
||
- TSIF_STS_CTL_PKT_READ_ERR |
|
||
- TSIF_STS_CTL_OVERFLOW |
|
||
- TSIF_STS_CTL_LOST_SYNC |
|
||
- TSIF_STS_CTL_TIMEOUT))) {
|
||
- return IRQ_NONE;
|
||
- }
|
||
-
|
||
- if (sts_ctl & TSIF_STS_CTL_PKT_WRITE_ERR) {
|
||
- src_bitmask |= TSPP2_SRC_EVENT_TSIF_PKT_WRITE_ERROR;
|
||
- tsif_device->stat_pkt_write_err++;
|
||
- }
|
||
-
|
||
- if (sts_ctl & TSIF_STS_CTL_PKT_READ_ERR) {
|
||
- src_bitmask |= TSPP2_SRC_EVENT_TSIF_PKT_READ_ERROR;
|
||
- tsif_device->stat_pkt_read_err++;
|
||
- }
|
||
-
|
||
- if (sts_ctl & TSIF_STS_CTL_OVERFLOW) {
|
||
- src_bitmask |= TSPP2_SRC_EVENT_TSIF_OVERFLOW;
|
||
- tsif_device->stat_overflow++;
|
||
- }
|
||
-
|
||
- if (sts_ctl & TSIF_STS_CTL_LOST_SYNC) {
|
||
- src_bitmask |= TSPP2_SRC_EVENT_TSIF_LOST_SYNC;
|
||
- tsif_device->stat_lost_sync++;
|
||
- }
|
||
-
|
||
- if (sts_ctl & TSIF_STS_CTL_TIMEOUT) {
|
||
- src_bitmask |= TSPP2_SRC_EVENT_TSIF_TIMEOUT;
|
||
- tsif_device->stat_timeout++;
|
||
- }
|
||
-
|
||
- /* Invoke user TSIF source callbacks if registered for these events */
|
||
- src = &tsif_device->dev->tsif_sources[tsif_device->hw_index];
|
||
-
|
||
- spin_lock_irqsave(&src->device->spinlock, flags);
|
||
-
|
||
- if (src->event_callback && (src->event_bitmask & src_bitmask))
|
||
- tspp2_event_work_prepare(tsif_device->dev, src->event_callback,
|
||
- src->event_cookie, (src->event_bitmask & src_bitmask));
|
||
-
|
||
- spin_unlock_irqrestore(&src->device->spinlock, flags);
|
||
-
|
||
- writel_relaxed(sts_ctl, tsif_device->base + TSPP2_TSIF_STS_CTL);
|
||
- /*
|
||
- * Before returning IRQ_HANDLED to the generic interrupt handling
|
||
- * framework, we need to make sure all operations, including clearing of
|
||
- * interrupt status registers in the hardware, are performed.
|
||
- * Thus a barrier after clearing the interrupt status register
|
||
- * is required to guarantee that the interrupt status register has
|
||
- * really been cleared by the time we return from this handler.
|
||
- */
|
||
- wmb();
|
||
-
|
||
- return IRQ_HANDLED;
|
||
-}
|
||
-
|
||
-/**
|
||
- * msm_tspp2_map_irqs() - Get and request IRQs.
|
||
- *
|
||
- * @pdev: Platform device, containing platform information.
|
||
- * @device: TSPP2 device.
|
||
- *
|
||
- * Helper function to get IRQ numbers from the platform device and request
|
||
- * the IRQs (i.e., set interrupt handlers) for the TSPP2 and TSIF interrupts.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int msm_tspp2_map_irqs(struct platform_device *pdev,
|
||
- struct tspp2_device *device)
|
||
-{
|
||
- int rc;
|
||
- int i;
|
||
-
|
||
- /* get IRQ numbers from platform information */
|
||
-
|
||
- rc = platform_get_irq_byname(pdev, "TSPP2");
|
||
- if (rc > 0) {
|
||
- device->tspp2_irq = rc;
|
||
- } else {
|
||
- dev_err(&pdev->dev, "%s: Failed to get TSPP2 IRQ", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- rc = platform_get_irq_byname(pdev, "TSIF0");
|
||
- if (rc > 0) {
|
||
- device->tsif_devices[0].tsif_irq = rc;
|
||
- } else {
|
||
- dev_err(&pdev->dev, "%s: Failed to get TSIF0 IRQ", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- rc = platform_get_irq_byname(pdev, "TSIF1");
|
||
- if (rc > 0) {
|
||
- device->tsif_devices[1].tsif_irq = rc;
|
||
- } else {
|
||
- dev_err(&pdev->dev, "%s: Failed to get TSIF1 IRQ", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- rc = platform_get_irq_byname(pdev, "TSPP2_BAM");
|
||
- if (rc > 0) {
|
||
- device->bam_irq = rc;
|
||
- } else {
|
||
- dev_err(&pdev->dev,
|
||
- "%s: Failed to get TSPP2 BAM IRQ", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- rc = request_irq(device->tspp2_irq, tspp2_isr, IRQF_SHARED,
|
||
- dev_name(&pdev->dev), device);
|
||
- if (rc) {
|
||
- dev_err(&pdev->dev,
|
||
- "%s: Failed to request TSPP2 IRQ %d : %d",
|
||
- __func__, device->tspp2_irq, rc);
|
||
- goto request_irq_err;
|
||
- }
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) {
|
||
- rc = request_irq(device->tsif_devices[i].tsif_irq,
|
||
- tsif_isr, IRQF_SHARED,
|
||
- dev_name(&pdev->dev), &device->tsif_devices[i]);
|
||
- if (rc) {
|
||
- dev_warn(&pdev->dev,
|
||
- "%s: Failed to request TSIF%d IRQ: %d",
|
||
- __func__, i, rc);
|
||
- device->tsif_devices[i].tsif_irq = 0;
|
||
- }
|
||
- }
|
||
-
|
||
- return 0;
|
||
-
|
||
-request_irq_err:
|
||
- device->tspp2_irq = 0;
|
||
- device->tsif_devices[0].tsif_irq = 0;
|
||
- device->tsif_devices[1].tsif_irq = 0;
|
||
- device->bam_irq = 0;
|
||
-
|
||
- return -EINVAL;
|
||
-}
|
||
-
|
||
-/* Device driver probe function */
|
||
-static int msm_tspp2_probe(struct platform_device *pdev)
|
||
-{
|
||
- int rc = 0;
|
||
- struct msm_tspp2_platform_data *data;
|
||
- struct tspp2_device *device;
|
||
- struct msm_bus_scale_pdata *tspp2_bus_pdata = NULL;
|
||
-
|
||
- if (pdev->dev.of_node) {
|
||
- /* Get information from device tree */
|
||
- data = msm_tspp2_dt_to_pdata(pdev);
|
||
- /* get device ID */
|
||
- rc = of_property_read_u32(pdev->dev.of_node,
|
||
- "cell-index", &pdev->id);
|
||
- if (rc)
|
||
- pdev->id = -1;
|
||
-
|
||
- tspp2_bus_pdata = msm_bus_cl_get_pdata(pdev);
|
||
- pdev->dev.platform_data = data;
|
||
- } else {
|
||
- /* Get information from platform data */
|
||
- data = pdev->dev.platform_data;
|
||
- }
|
||
- if (!data) {
|
||
- pr_err("%s: Platform data not available\n", __func__);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- /* Verify device id is valid */
|
||
- if ((pdev->id < 0) || (pdev->id >= TSPP2_NUM_DEVICES)) {
|
||
- pr_err("%s: Invalid device ID %d\n", __func__, pdev->id);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- device = devm_kzalloc(&pdev->dev,
|
||
- sizeof(struct tspp2_device),
|
||
- GFP_KERNEL);
|
||
- if (!device) {
|
||
- pr_err("%s: Failed to allocate memory for device\n", __func__);
|
||
- return -ENOMEM;
|
||
- }
|
||
- platform_set_drvdata(pdev, device);
|
||
- device->pdev = pdev;
|
||
- device->dev = &pdev->dev;
|
||
- device->dev_id = pdev->id;
|
||
- device->opened = 0;
|
||
-
|
||
- /* Register bus client */
|
||
- if (tspp2_bus_pdata) {
|
||
- device->bus_client =
|
||
- msm_bus_scale_register_client(tspp2_bus_pdata);
|
||
- if (!device->bus_client)
|
||
- pr_err("%s: Unable to register bus client\n", __func__);
|
||
- } else {
|
||
- pr_err("%s: Platform bus client data not available. Continue anyway...\n",
|
||
- __func__);
|
||
- }
|
||
-
|
||
- rc = msm_tspp2_iommu_info_get(pdev, device);
|
||
- if (rc) {
|
||
- pr_err("%s: Failed to get IOMMU information\n", __func__);
|
||
- goto err_bus_client;
|
||
- }
|
||
-
|
||
- rc = msm_tspp2_clocks_setup(pdev, device);
|
||
- if (rc)
|
||
- goto err_clocks_setup;
|
||
-
|
||
- rc = msm_tspp2_map_io_memory(pdev, device);
|
||
- if (rc)
|
||
- goto err_map_io_memory;
|
||
-
|
||
- rc = msm_tspp2_map_irqs(pdev, device);
|
||
- if (rc)
|
||
- goto err_map_irq;
|
||
-
|
||
- mutex_init(&device->mutex);
|
||
-
|
||
- tspp2_devices[pdev->id] = device;
|
||
-
|
||
- tspp2_debugfs_init(device);
|
||
-
|
||
- return rc;
|
||
-
|
||
-err_map_irq:
|
||
- iounmap(device->base);
|
||
- iounmap(device->tsif_devices[0].base);
|
||
- iounmap(device->tsif_devices[1].base);
|
||
- iounmap(device->bam_props.virt_addr);
|
||
-
|
||
-err_map_io_memory:
|
||
- tspp2_clocks_put(device);
|
||
-
|
||
-err_clocks_setup:
|
||
- msm_tspp2_iommu_info_free(device);
|
||
-
|
||
-err_bus_client:
|
||
- if (device->bus_client)
|
||
- msm_bus_scale_unregister_client(device->bus_client);
|
||
-
|
||
- return rc;
|
||
-}
|
||
-
|
||
-/* Device driver remove function */
|
||
-static int msm_tspp2_remove(struct platform_device *pdev)
|
||
-{
|
||
- int i;
|
||
- int rc = 0;
|
||
- struct tspp2_device *device = platform_get_drvdata(pdev);
|
||
-
|
||
- tspp2_debugfs_exit(device);
|
||
-
|
||
- if (device->tspp2_irq)
|
||
- free_irq(device->tspp2_irq, device);
|
||
-
|
||
- for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++)
|
||
- if (device->tsif_devices[i].tsif_irq)
|
||
- free_irq(device->tsif_devices[i].tsif_irq,
|
||
- &device->tsif_devices[i]);
|
||
-
|
||
- /* Unmap memory */
|
||
- iounmap(device->base);
|
||
- iounmap(device->tsif_devices[0].base);
|
||
- iounmap(device->tsif_devices[1].base);
|
||
- iounmap(device->bam_props.virt_addr);
|
||
-
|
||
- msm_tspp2_iommu_info_free(device);
|
||
-
|
||
- if (device->bus_client)
|
||
- msm_bus_scale_unregister_client(device->bus_client);
|
||
-
|
||
- mutex_destroy(&device->mutex);
|
||
-
|
||
- tspp2_clocks_put(device);
|
||
-
|
||
- return rc;
|
||
-}
|
||
-
|
||
-/* Power Management */
|
||
-
|
||
-static int tspp2_runtime_suspend(struct device *dev)
|
||
-{
|
||
- int ret = 0;
|
||
- struct tspp2_device *device;
|
||
- struct platform_device *pdev;
|
||
-
|
||
- /*
|
||
- * HW manages power collapse automatically.
|
||
- * Disabling AHB and Core clocsk and "cancelling" bus bandwidth voting.
|
||
- */
|
||
-
|
||
- pdev = container_of(dev, struct platform_device, dev);
|
||
- device = platform_get_drvdata(pdev);
|
||
-
|
||
- mutex_lock(&device->mutex);
|
||
-
|
||
- if (!device->opened)
|
||
- ret = -EPERM;
|
||
- else
|
||
- ret = tspp2_reg_clock_stop(device);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- dev_dbg(dev, "%s\n", __func__);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-static int tspp2_runtime_resume(struct device *dev)
|
||
-{
|
||
- int ret = 0;
|
||
- struct tspp2_device *device;
|
||
- struct platform_device *pdev;
|
||
-
|
||
- /*
|
||
- * HW manages power collapse automatically.
|
||
- * Enabling AHB and Core clocks to allow access to unit registers,
|
||
- * and voting for the required bus bandwidth for register access.
|
||
- */
|
||
-
|
||
- pdev = container_of(dev, struct platform_device, dev);
|
||
- device = platform_get_drvdata(pdev);
|
||
-
|
||
- mutex_lock(&device->mutex);
|
||
-
|
||
- if (!device->opened)
|
||
- ret = -EPERM;
|
||
- else
|
||
- ret = tspp2_reg_clock_start(device);
|
||
-
|
||
- mutex_unlock(&device->mutex);
|
||
-
|
||
- dev_dbg(dev, "%s\n", __func__);
|
||
-
|
||
- return ret;
|
||
-}
|
||
-
|
||
-static const struct dev_pm_ops tspp2_dev_pm_ops = {
|
||
- .runtime_suspend = tspp2_runtime_suspend,
|
||
- .runtime_resume = tspp2_runtime_resume,
|
||
-};
|
||
-
|
||
-/* Platform driver information */
|
||
-
|
||
-static struct of_device_id msm_tspp2_match_table[] = {
|
||
- {.compatible = "qcom,msm_tspp2"},
|
||
- {}
|
||
-};
|
||
-
|
||
-static struct platform_driver msm_tspp2_driver = {
|
||
- .probe = msm_tspp2_probe,
|
||
- .remove = msm_tspp2_remove,
|
||
- .driver = {
|
||
- .name = "msm_tspp2",
|
||
- .pm = &tspp2_dev_pm_ops,
|
||
- .of_match_table = msm_tspp2_match_table,
|
||
- },
|
||
-};
|
||
-
|
||
-/**
|
||
- * tspp2_module_init() - TSPP2 driver module init function.
|
||
- *
|
||
- * Return 0 on success, error value otherwise.
|
||
- */
|
||
-static int __init tspp2_module_init(void)
|
||
-{
|
||
- int rc;
|
||
-
|
||
- rc = platform_driver_register(&msm_tspp2_driver);
|
||
- if (rc)
|
||
- pr_err("%s: platform_driver_register failed: %d\n",
|
||
- __func__, rc);
|
||
-
|
||
- return rc;
|
||
-}
|
||
-
|
||
-/**
|
||
- * tspp2_module_exit() - TSPP2 driver module exit function.
|
||
- */
|
||
-static void __exit tspp2_module_exit(void)
|
||
-{
|
||
- platform_driver_unregister(&msm_tspp2_driver);
|
||
-}
|
||
-
|
||
-module_init(tspp2_module_init);
|
||
-module_exit(tspp2_module_exit);
|
||
-
|
||
-MODULE_DESCRIPTION("TSPP2 (Transport Stream Packet Processor v2) platform device driver");
|
||
-MODULE_LICENSE("GPL v2");
|
||
--
|
||
cgit v1.1
|
||
|