From 921b60c4dc140eec57c11284214abba845d5460e Mon Sep 17 00:00:00 2001 From: Liron Kuch Date: Sun, 6 Sep 2015 11:19:39 +0300 Subject: [PATCH] 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 --- 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 11e74a2ef54e..000000000000 --- 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 006c6883e6dc..000000000000 --- 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 1233d6d13aa5..5e72b0d1fade 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 ec3142eca14f..000000000000 --- 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 -#include -#include -#include /* Device drivers need this */ -#include /* Char device drivers need that */ -#include /* for KERN_INFO */ -#include -#include /* for completion signaling after interrupts */ -#include /* for copy from/to user in the ioctls */ -#include -#include -#include /* parsing device tree data */ -#include -#include -#include /* gpios definitions */ -#include /* pinctrl API */ -#include -#include /* wait() macros, sleeping */ -#include /* Externally defined globals */ -#include /* poll() file op */ -#include /* IO macros */ -#include -#include /* ion_map_iommu */ -#include -#include -#include /* kfree, kzalloc */ -#include /* debugfs support */ -#include /* debugfs support */ -#include /* debugfs support */ -#include /* gdsc */ -#include /* bus client */ -#include /* usleep function */ -/* TODO: include 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/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 1f51dca14a22..000000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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");